修复比赛修改密码之后,之前保存的 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 functools
import hashlib
import time
from problem.models import Problem from problem.models import Problem
from contest.models import Contest, ContestType, ContestStatus, ContestRuleType from contest.models import Contest, ContestType, ContestStatus, ContestRuleType
from utils.api import JSONResponse, APIError from utils.api import JSONResponse, APIError
from utils.constants import CONTEST_PASSWORD_SESSION_KEY
from .models import ProblemPermission from .models import ProblemPermission
@ -55,6 +59,32 @@ class problem_permission_required(admin_role_required):
return True 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"): def check_contest_permission(check_type="details"):
""" """
只供Class based view 使用检查用户是否有权进入该contest, check_type 可选 details, problems, ranks, submissions 只供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: if self.contest.contest_type == ContestType.PASSWORD_PROTECTED_CONTEST:
# password error # password error
if self.contest.id not in request.session.get("accessible_contests", []): if not check_contest_password(request.session.get(CONTEST_PASSWORD_SESSION_KEY, {}).get(self.contest.id), self.contest.password):
return self.error("Password is required.") return self.error("Wrong password or password expired")
# regular user get contest problems, ranks etc. before contest started # regular user get contest problems, ranks etc. before contest started
if self.contest.status == ContestStatus.CONTEST_NOT_START and check_type != "details": 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 problem.models import Problem
from utils.api import APIView, validate_serializer 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 utils.shortcuts import datetime2str, check_is_id
from account.models import AdminType 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 utils.constants import ContestRuleType, ContestStatus
from ..models import ContestAnnouncement, Contest, OIContestRank, ACMContestRank 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) contest = Contest.objects.get(id=data["contest_id"], visible=True, password__isnull=False)
except Contest.DoesNotExist: except Contest.DoesNotExist:
return self.error("Contest does not exist") return self.error("Contest does not exist")
if contest.password != data["password"]: if not check_contest_password(data["password"], contest.password):
return self.error("Wrong password") return self.error("Wrong password or password expired")
# password verify OK. # password verify OK.
if "accessible_contests" not in request.session: if CONTEST_PASSWORD_SESSION_KEY not in request.session:
request.session["accessible_contests"] = [] request.session[CONTEST_PASSWORD_SESSION_KEY] = {}
request.session["accessible_contests"].append(contest.id) request.session[CONTEST_PASSWORD_SESSION_KEY][contest.id] = data["password"]
# https://docs.djangoproject.com/en/dev/topics/http/sessions/#when-sessions-are-saved # https://docs.djangoproject.com/en/dev/topics/http/sessions/#when-sessions-are-saved
request.session.modified = True request.session.modified = True
return self.success(True) return self.success(True)
@ -94,7 +94,12 @@ class ContestAccessAPI(APIView):
contest_id = request.GET.get("contest_id") contest_id = request.GET.get("contest_id")
if not contest_id: if not contest_id:
return self.error() 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): class ContestRankAPI(APIView):

View File

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