Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix problem solution access check; fixes #1371 #1425

Merged
merged 1 commit into from
Jun 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -150,8 +149,10 @@ def page_title(self):

def is_accessible_by(self, user):
try:
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
7 changes: 2 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,10 +118,7 @@ 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()
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