Skip to content

Commit

Permalink
Protect stateful admin endpoints against CSRF
Browse files Browse the repository at this point in the history
  • Loading branch information
magnified103 authored and quantum5 committed Jul 31, 2024
1 parent 2b36e04 commit 8cebced
Show file tree
Hide file tree
Showing 8 changed files with 36 additions and 8 deletions.
7 changes: 6 additions & 1 deletion judge/admin/contest.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
from django.shortcuts import get_object_or_404
from django.urls import path, reverse, reverse_lazy
from django.utils import timezone
from django.utils.decorators import method_decorator
from django.utils.html import format_html
from django.utils.translation import gettext_lazy as _, ngettext
from django.views.decorators.http import require_POST
from reversion.admin import VersionAdmin

from django_ace import AceWidget
Expand Down Expand Up @@ -73,7 +75,7 @@ class ContestProblemInline(SortableInlineAdminMixin, admin.TabularInline):
def rejudge_column(self, obj):
if obj.id is None:
return ''
return format_html('<a class="button rejudge-link" href="{0}">{1}</a>',
return format_html('<a class="button rejudge-link action-link" href="{0}">{1}</a>',
reverse('admin:judge_contest_rejudge', args=(obj.contest.id, obj.id)), _('Rejudge'))


Expand Down Expand Up @@ -272,6 +274,7 @@ def get_urls(self):
path('<int:contest_id>/judge/<int:problem_id>/', self.rejudge_view, name='judge_contest_rejudge'),
] + super(ContestAdmin, self).get_urls()

@method_decorator(require_POST)
def rejudge_view(self, request, contest_id, problem_id):
contest = get_object_or_404(Contest, id=contest_id)
if not self.has_change_permission(request, contest):
Expand All @@ -285,6 +288,7 @@ def rejudge_view(self, request, contest_id, problem_id):
len(queryset)) % len(queryset))
return HttpResponseRedirect(reverse('admin:judge_contest_change', args=(contest_id,)))

@method_decorator(require_POST)
def rate_all_view(self, request):
if not request.user.has_perm('judge.contest_rating'):
raise PermissionDenied()
Expand All @@ -296,6 +300,7 @@ def rate_all_view(self, request):
rate_contest(contest)
return HttpResponseRedirect(reverse('admin:judge_contest_changelist'))

@method_decorator(require_POST)
def rate_view(self, request, id):
if not request.user.has_perm('judge.contest_rating'):
raise PermissionDenied()
Expand Down
5 changes: 5 additions & 0 deletions judge/admin/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.urls import path, reverse
from django.utils.decorators import method_decorator
from django.utils.html import format_html
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _
from django.views.decorators.http import require_POST
from reversion.admin import VersionAdmin

from django_ace import AceWidget
Expand Down Expand Up @@ -85,18 +87,21 @@ def disconnect_judge(self, id, force=False):
judge.disconnect(force=force)
return HttpResponseRedirect(reverse('admin:judge_judge_changelist'))

@method_decorator(require_POST)
def disconnect_view(self, request, id):
judge = get_object_or_404(Judge, id=id)
if not self.has_change_permission(request, judge):
raise PermissionDenied()
return self.disconnect_judge(id)

@method_decorator(require_POST)
def terminate_view(self, request, id):
judge = get_object_or_404(Judge, id=id)
if not self.has_change_permission(request, judge):
raise PermissionDenied()
return self.disconnect_judge(id, force=True)

@method_decorator(require_POST)
def disable_view(self, request, id):
judge = get_object_or_404(Judge, id=id)
if not self.has_change_permission(request, judge):
Expand Down
3 changes: 3 additions & 0 deletions judge/admin/submission.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.urls import path, reverse
from django.utils.decorators import method_decorator
from django.utils.html import format_html
from django.utils.translation import gettext, gettext_lazy as _, ngettext, pgettext
from django.views.decorators.http import require_POST
from reversion.admin import VersionAdmin

from django_ace import AceWidget
Expand Down Expand Up @@ -250,6 +252,7 @@ def get_urls(self):
path('<int:id>/judge/', self.judge_view, name='judge_submission_rejudge'),
] + super(SubmissionAdmin, self).get_urls()

@method_decorator(require_POST)
def judge_view(self, request, id):
if not request.user.has_perm('judge.rejudge_submission') or not request.user.has_perm('judge.edit_own_problem'):
raise PermissionDenied()
Expand Down
15 changes: 15 additions & 0 deletions templates/admin/base_site.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{% extends "admin/base_site.html" %}

{% block pretitle %}{{ block.super }}
<form style="display: none" id="empty-action-form" method="post">
{% csrf_token %}
</form>
<script>
django.jQuery(function ($) {
$('.action-link').on('click', function () {
$('#empty-action-form').attr('action', this.href).trigger('submit');
return false;
});
});
</script>
{% endblock %}
2 changes: 1 addition & 1 deletion templates/admin/judge/contest/change_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
{% block after_field_sets %}{{ block.super }}
{% if original and original.is_rated and original.ended and perms.judge.contest_rating %}
<a style="display: none" title="{% trans "Rate" %}" href="{% url 'admin:judge_contest_rate' original.pk %}"
class="button rerate-link">
class="button rerate-link action-link">
<i class="fa fa-lg fa-signal"></i>
<span class="text">{% trans "Rate" %}</span>
</a>
Expand Down
2 changes: 1 addition & 1 deletion templates/admin/judge/contest/change_list.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
{{ block.super }}
{% if not is_popup and perms.judge.contest_rating %}
<li>
<a href="{% url 'admin:judge_contest_rate_all' %}" class="ratealllink">
<a href="{% url 'admin:judge_contest_rate_all' %}" class="ratealllink action-link">
<i class="fa fa-signal"></i> {% trans "Rate all ratable contests" %}
</a>
</li>
Expand Down
8 changes: 4 additions & 4 deletions templates/admin/judge/judge/change_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,24 @@
{% block after_field_sets %}{{ block.super }}
{% if original %}
<a style="display: none" title="{% trans "Disconnect" %}" href="{% url 'admin:judge_judge_disconnect' original.pk %}"
class="button disconnect-link">
class="button disconnect-link action-link">
<i class="fa fa-lg fa-power-off"></i>
<span class="text">{% trans "Disconnect" %}</span>
</a>
<a style="display: none" title="{% trans "Terminate" %}" href="{% url 'admin:judge_judge_terminate' original.pk %}"
class="button terminate-link">
class="button terminate-link action-link">
<i class="fa fa-lg fa-plug"></i>
<span class="text">{% trans "Terminate" %}</span>
</a>
{% if not original.is_disabled %}
<a style="display: none" title="{% trans "Disable" %}" href="{% url 'admin:judge_judge_disable' original.pk %}"
class="button disable-link">
class="button disable-link action-link">
<i class="fa fa-lg fa-times"></i>
<span class="text">{% trans "Disable" %}</span>
</a>
{% else %}
<a style="display: none" title="{% trans "Enable" %}" href="{% url 'admin:judge_judge_disable' original.pk %}"
class="button disable-link">
class="button disable-link action-link">
<i class="fa fa-lg fa-check"></i>
<span class="text">{% trans "Enable" %}</span>
</a>
Expand Down
2 changes: 1 addition & 1 deletion templates/admin/judge/submission/change_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
{% block after_field_sets %}{{ block.super }}
{% if original and not original.is_locked %}
<a style="display: none" title="{% trans "Rejudge" %}" href="{% url 'admin:judge_submission_rejudge' original.pk %}"
class="button rejudgelink">
class="button rejudgelink action-link">
<i class="fa fa-lg fa-refresh"></i>
<span class="text">{% trans "Rejudge" %}</span>
</a>
Expand Down

0 comments on commit 8cebced

Please sign in to comment.