Skip to content

Commit

Permalink
Fix problem solution access check; fixes DMOJ#1371
Browse files Browse the repository at this point in the history
  • Loading branch information
Ninjaclasher committed Jun 4, 2020
1 parent c6e7a38 commit e026f4e
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 31 deletions.
49 changes: 25 additions & 24 deletions judge/models/comment.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

from judge.models.contest import Contest
from judge.models.interface import BlogPost
from judge.models.problem import Problem
from judge.models.problem import Problem, Solution
from judge.models.profile import Profile
from judge.utils.cachedict import CacheDict

Expand Down Expand Up @@ -62,10 +62,12 @@ def most_recent(cls, user, n, batch=None):
.defer('author__about', 'body').order_by('-id')

problem_cache = CacheDict(lambda code: Problem.objects.defer('description', 'summary').get(code=code))
solution_cache = CacheDict(lambda code: Solution.objects.defer('content').get(problem__code=code))
contest_cache = CacheDict(lambda key: Contest.objects.defer('description').get(key=key))
blog_cache = CacheDict(lambda id: BlogPost.objects.defer('summary', 'content').get(id=id))

problem_access = CacheDict(lambda code: problem_cache[code].is_accessible_by(user))
solution_access = CacheDict(lambda code: problem_access[code] and solution_cache[code].is_accessible_by(user))
contest_access = CacheDict(lambda key: contest_cache[key].is_accessible_by(user))
blog_access = CacheDict(lambda id: blog_cache[id].can_see(user))

Expand All @@ -78,29 +80,26 @@ def most_recent(cls, user, n, batch=None):
break
for comment in slice:
page_key = comment.page[2:]
if comment.page.startswith('p:') or comment.page.startswith('s:'):
try:
if problem_access[page_key]:
comment.page_title = problem_cache[page_key].name
output.append(comment)
except Problem.DoesNotExist:
pass
elif comment.page.startswith('c:'):
try:
if contest_access[page_key]:
comment.page_title = contest_cache[page_key].name
output.append(comment)
except Contest.DoesNotExist:
pass
elif comment.page.startswith('b:'):
try:
if blog_access[page_key]:
comment.page_title = blog_cache[page_key].title
output.append(comment)
except BlogPost.DoesNotExist:
pass
try:
if comment.page.startswith('p:'):
has_access = problem_access[page_key]
comment.page_title = problem_cache[page_key].name
elif comment.page.startswith('s:'):
has_access = solution_access[page_key]
comment.page_title = _('Editorial for %s') % problem_cache[page_key].name
elif comment.page.startswith('c:'):
has_access = contest_access[page_key]
comment.page_title = contest_cache[page_key].name
elif comment.page.startswith('b:'):
has_access = blog_access[page_key]
comment.page_title = blog_cache[page_key].title
else:
has_access = True
except ObjectDoesNotExist:
pass
else:
output.append(comment)
if has_access:
output.append(comment)
if len(output) >= n:
return output
return output
Expand Down Expand Up @@ -149,8 +148,10 @@ def page_title(self):
return self.get_page_title(self.page)

def is_accessible_by(self, user):
if self.page.startswith('p:') or self.page.startswith('s:'):
if self.page.startswith('p:'):
return Problem.objects.get(code=self.page[2:]).is_accessible_by(user)
elif self.page.startswith('s:'):
return Solution.objects.get(problem__code=self.page[2:]).is_accessible_by(user)
elif self.page.startswith('c:'):
return Contest.objects.get(key=self.page[2:]).is_accessible_by(user)
elif self.page.startswith('b:'):
Expand Down
10 changes: 10 additions & 0 deletions judge/models/problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from django.db.models.expressions import RawSQL
from django.db.models.functions import Coalesce
from django.urls import reverse
from django.utils import timezone
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _

Expand Down Expand Up @@ -460,6 +461,15 @@ def get_absolute_url(self):
def __str__(self):
return _('Editorial for %s') % self.problem.name

def is_accessible_by(self, user):
if self.is_public and self.publish_on < timezone.now():
return True
if user.has_perm('judge.see_private_solution'):
return True
if self.problem.is_editable_by(user):
return True
return False

class Meta:
permissions = (
('see_private_solution', _('See hidden solutions')),
Expand Down
8 changes: 3 additions & 5 deletions judge/views/problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from django.shortcuts import get_object_or_404
from django.template.loader import get_template
from django.urls import reverse
from django.utils import timezone, translation
from django.utils import translation
from django.utils.functional import cached_property
from django.utils.html import escape, format_html
from django.utils.safestring import mark_safe
Expand Down Expand Up @@ -118,11 +118,9 @@ def get_context_data(self, **kwargs):

solution = get_object_or_404(Solution, problem=self.object)

if (not solution.is_public or solution.publish_on > timezone.now()) and \
not self.request.user.has_perm('judge.see_private_solution') or \
(self.request.user.is_authenticated and
self.request.profile.current_contest):
if not solution.is_accessible_by(self.request.user) or self.request.in_contest:
raise Http404()

context['solution'] = solution
context['has_solved_problem'] = self.object.id in self.get_completed_problems()
return context
Expand Down
3 changes: 1 addition & 2 deletions templates/problem/problem.html
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,7 @@ <h2 style="color:#393630; display: inline-block">{{ title }}</h2>
{% endif %}
<div><a href="{{ url('chronological_submissions', problem.code) }}">{{ _('All submissions') }}</a></div>
<div><a href="{{ url('ranked_submissions', problem.code) }}">{{ _('Best submissions') }}</a></div>
{% if editorial and editorial.is_public and
not (request.user.is_authenticated and request.profile.current_contest) %}
{% if (editorial and editorial.is_accessible_by(request.user)) and not request.in_contest %}
<hr>
<div><a href="{{ url('problem_editorial', problem.code) }}">{{ _('Read editorial') }}</a></div>
{% endif %}
Expand Down

0 comments on commit e026f4e

Please sign in to comment.