修复比赛修改密码之后,之前保存的 session 仍然可以使用的问题

This commit is contained in:
virusdefender 2019-09-22 16:45:25 +08:00
parent 0401468ff3
commit d57cf64afa
3 changed files with 48 additions and 10 deletions

View File

@ -1,7 +1,11 @@
import functools
import hashlib
import time
from problem.models import Problem
from contest.models import Contest, ContestType, ContestStatus, ContestRuleType
from utils.api import JSONResponse, APIError
from utils.constants import CONTEST_PASSWORD_SESSION_KEY
from .models import ProblemPermission
@ -55,6 +59,32 @@ class problem_permission_required(admin_role_required):
return True
def check_contest_password(password, contest_password):
if not (password and contest_password):
return False
if password == contest_password:
return True
else:
# sig#timestamp 这种形式的密码也可以,但是在界面上没提供支持
# sig = sha256(contest_password + timestamp)[:8]
if "#" in password:
s = password.split("#")
if len(s) != 2:
return False
sig, ts = s[0], s[1]
if sig == hashlib.sha256((contest_password + ts).encode("utf-8")).hexdigest()[:8]:
try:
ts = int(ts)
except Exception:
return False
return int(time.time()) < ts
else:
return False
else:
return False
def check_contest_permission(check_type="details"):
"""
只供Class based view 使用检查用户是否有权进入该contest, check_type 可选 details, problems, ranks, submissions
@ -89,8 +119,8 @@ def check_contest_permission(check_type="details"):
if self.contest.contest_type == ContestType.PASSWORD_PROTECTED_CONTEST:
# password error
if self.contest.id not in request.session.get("accessible_contests", []):
return self.error("Password is required.")
if not check_contest_password(request.session.get(CONTEST_PASSWORD_SESSION_KEY, {}).get(self.contest.id), self.contest.password):
return self.error("Wrong password or password expired")
# regular user get contest problems, ranks etc. before contest started
if self.contest.status == ContestStatus.CONTEST_NOT_START and check_type != "details":

View File

@ -7,10 +7,10 @@ from django.core.cache import cache
from problem.models import Problem
from utils.api import APIView, validate_serializer
from utils.constants import CacheKey
from utils.constants import CacheKey, CONTEST_PASSWORD_SESSION_KEY
from utils.shortcuts import datetime2str, check_is_id
from account.models import AdminType
from account.decorators import login_required, check_contest_permission
from account.decorators import login_required, check_contest_permission, check_contest_password
from utils.constants import ContestRuleType, ContestStatus
from ..models import ContestAnnouncement, Contest, OIContestRank, ACMContestRank
@ -76,13 +76,13 @@ class ContestPasswordVerifyAPI(APIView):
contest = Contest.objects.get(id=data["contest_id"], visible=True, password__isnull=False)
except Contest.DoesNotExist:
return self.error("Contest does not exist")
if contest.password != data["password"]:
return self.error("Wrong password")
if not check_contest_password(data["password"], contest.password):
return self.error("Wrong password or password expired")
# password verify OK.
if "accessible_contests" not in request.session:
request.session["accessible_contests"] = []
request.session["accessible_contests"].append(contest.id)
if CONTEST_PASSWORD_SESSION_KEY not in request.session:
request.session[CONTEST_PASSWORD_SESSION_KEY] = {}
request.session[CONTEST_PASSWORD_SESSION_KEY][contest.id] = data["password"]
# https://docs.djangoproject.com/en/dev/topics/http/sessions/#when-sessions-are-saved
request.session.modified = True
return self.success(True)
@ -94,7 +94,12 @@ class ContestAccessAPI(APIView):
contest_id = request.GET.get("contest_id")
if not contest_id:
return self.error()
return self.success({"access": int(contest_id) in request.session.get("accessible_contests", [])})
try:
contest = Contest.objects.get(id=contest_id, visible=True, password__isnull=False)
except Contest.DoesNotExist:
return self.error("Contest does not exist")
session_pass = request.session.get(CONTEST_PASSWORD_SESSION_KEY, {}).get(contest.id)
return self.success({"access": check_contest_password(session_pass, contest.password)})
class ContestRankAPI(APIView):

View File

@ -31,3 +31,6 @@ class Difficulty(Choices):
LOW = "Low"
MID = "Mid"
HIGH = "High"
CONTEST_PASSWORD_SESSION_KEY = "contest_password"