OnlineJudge/account/decorators.py

150 lines
5.3 KiB
Python
Raw Permalink Normal View History

import functools
import hashlib
import time
from problem.models import Problem
2017-10-27 10:36:29 +00:00
from contest.models import Contest, ContestType, ContestStatus, ContestRuleType
2018-01-04 11:27:41 +00:00
from utils.api import JSONResponse, APIError
from utils.constants import CONTEST_PASSWORD_SESSION_KEY
2018-01-04 11:27:41 +00:00
from .models import ProblemPermission
class BasePermissionDecorator(object):
def __init__(self, func):
self.func = func
def __get__(self, obj, obj_type):
return functools.partial(self.__call__, obj)
2016-10-29 18:17:35 +00:00
def error(self, data):
2016-11-19 04:32:23 +00:00
return JSONResponse.response({"error": "permission-denied", "data": data})
2016-10-29 18:17:35 +00:00
def __call__(self, *args, **kwargs):
2016-10-29 18:17:35 +00:00
self.request = args[1]
if self.check_permission():
if self.request.user.is_disabled:
2017-04-18 18:03:48 +00:00
return self.error("Your account is disabled")
return self.func(*args, **kwargs)
else:
2017-10-27 10:36:29 +00:00
return self.error("Please login first")
def check_permission(self):
raise NotImplementedError()
class login_required(BasePermissionDecorator):
def check_permission(self):
2019-03-11 03:25:10 +00:00
return self.request.user.is_authenticated
class super_admin_required(BasePermissionDecorator):
def check_permission(self):
user = self.request.user
2019-03-11 03:25:10 +00:00
return user.is_authenticated and user.is_super_admin()
class admin_role_required(BasePermissionDecorator):
def check_permission(self):
user = self.request.user
2019-03-11 03:25:10 +00:00
return user.is_authenticated and user.is_admin_role()
class problem_permission_required(admin_role_required):
def check_permission(self):
if not super(problem_permission_required, self).check_permission():
return False
if self.request.user.problem_permission == ProblemPermission.NONE:
return False
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
2017-10-27 10:36:29 +00:00
def check_contest_permission(check_type="details"):
"""
2017-10-27 10:36:29 +00:00
只供Class based view 使用检查用户是否有权进入该contest, check_type 可选 details, problems, ranks, submissions
若通过验证在view中可通过self.contest获得该contest
"""
2017-10-27 10:36:29 +00:00
def decorator(func):
def _check_permission(*args, **kwargs):
self = args[0]
request = args[1]
user = request.user
if request.data.get("contest_id"):
contest_id = request.data["contest_id"]
2017-10-27 10:36:29 +00:00
else:
contest_id = request.GET.get("contest_id")
if not contest_id:
return self.error("Parameter error, contest_id is required")
2017-10-27 10:36:29 +00:00
try:
# use self.contest to avoid query contest again in view.
self.contest = Contest.objects.select_related("created_by").get(id=contest_id, visible=True)
except Contest.DoesNotExist:
return self.error("Contest %s doesn't exist" % contest_id)
2018-06-30 06:40:35 +00:00
# Anonymous
2019-03-11 03:25:10 +00:00
if not user.is_authenticated:
2018-06-30 06:40:35 +00:00
return self.error("Please login first.")
2017-10-27 10:36:29 +00:00
# creator or owner
2018-06-30 06:40:35 +00:00
if user.is_contest_admin(self.contest):
2017-10-27 10:36:29 +00:00
return func(*args, **kwargs)
if self.contest.contest_type == ContestType.PASSWORD_PROTECTED_CONTEST:
# password error
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")
2017-10-27 10:36:29 +00:00
2017-11-23 11:11:12 +00:00
# regular user get contest problems, ranks etc. before contest started
2017-10-27 10:36:29 +00:00
if self.contest.status == ContestStatus.CONTEST_NOT_START and check_type != "details":
return self.error("Contest has not started yet.")
# check does user have permission to get ranks, submissions in OI Contest
2017-10-27 10:36:29 +00:00
if self.contest.status == ContestStatus.CONTEST_UNDERWAY and self.contest.rule_type == ContestRuleType.OI:
if not self.contest.real_time_rank and (check_type == "ranks" or check_type == "submissions"):
return self.error(f"No permission to get {check_type}")
2017-07-20 07:52:11 +00:00
2017-10-27 10:36:29 +00:00
return func(*args, **kwargs)
return _check_permission
return decorator
2018-01-04 11:27:41 +00:00
def ensure_created_by(obj, user):
e = APIError(msg=f"{obj.__class__.__name__} does not exist")
if not user.is_admin_role():
raise e
if user.is_super_admin():
return
if isinstance(obj, Problem):
if not user.can_mgmt_all_problem() and obj.created_by != user:
raise e
elif obj.created_by != user:
raise e