Use CSRF token on forms to trigger a new builds (#3260)

* Use CSRF token on forms to trigger a new builds

Avoid "This endpoint is deprecated" raised when hitting and old
endpoint with security issues.

Now, each view that shows a form with the "Build" button uses the
BuildTriggerMixin to handle the POST request and trigger the new build.

Closes #3253

* Filter project by `for_admin_user`

* Use login_required as method_decorator for BuildTriggerMixin

The user must be logged in to trigger a build.
theme-0.6.2
Manuel Kaufmann 2017-11-15 15:30:52 -05:00 committed by Anthony
parent 1e55dfffc8
commit 4de678e5c4
5 changed files with 32 additions and 6 deletions

View File

@ -6,11 +6,14 @@ import logging
from django.shortcuts import get_object_or_404
from django.views.generic import ListView, DetailView
from django.http import HttpResponsePermanentRedirect
from django.http import HttpResponsePermanentRedirect, HttpResponseRedirect
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.core.urlresolvers import reverse
from django.utils.decorators import method_decorator
from readthedocs.builds.models import Build, Version
from readthedocs.core.utils import trigger_build
from readthedocs.projects.models import Project
from redis import Redis, ConnectionError
@ -33,7 +36,26 @@ class BuildBase(object):
return queryset
class BuildList(BuildBase, ListView):
class BuildTriggerMixin(object):
@method_decorator(login_required)
def post(self, request, project_slug):
project = get_object_or_404(
Project.objects.for_admin_user(self.request.user),
slug=project_slug
)
version_slug = request.POST.get('version_slug')
version = get_object_or_404(
Version,
project=project,
slug=version_slug,
)
trigger_build(project=project, version=version)
return HttpResponseRedirect(reverse('builds_project_list', args=[project.slug]))
class BuildList(BuildBase, BuildTriggerMixin, ListView):
def get_context_data(self, **kwargs):
context = super(BuildList, self).get_context_data(**kwargs)

View File

@ -26,6 +26,7 @@ import requests
from .base import ProjectOnboardMixin
from readthedocs.builds.constants import LATEST
from readthedocs.builds.models import Version
from readthedocs.builds.views import BuildTriggerMixin
from readthedocs.projects.models import Project, ImportedFile
from readthedocs.search.indexes import PageIndex
from readthedocs.search.views import LOG_TEMPLATE
@ -67,7 +68,7 @@ class ProjectIndex(ListView):
project_index = ProjectIndex.as_view()
class ProjectDetailView(ProjectOnboardMixin, DetailView):
class ProjectDetailView(BuildTriggerMixin, ProjectOnboardMixin, DetailView):
"""Display project onboard steps"""

View File

@ -40,7 +40,8 @@
{% if versions %}
<div class="module-header">
<div style="float:right;">
<form method="post" action="{% url "generic_build" project.pk %}">
<form method="post" action="{% url "builds_project_list" project.slug %}">
{% csrf_token %}
<ul class="build_a_version">
<li style="display: inline-block">
<input style="display: inline-block" type="submit" value="{% trans "Build Version:" %}">

View File

@ -59,7 +59,8 @@
<div class="build_a_version">
<h3>{% trans "Build a version" %}</h3>
<div class="version_right">
<form method="post" action="{% url "generic_build" project.pk %}">
<form method="post" action="{% url "projects_detail" project.slug %}">
{% csrf_token %}
<select id="id_version" name="version_slug">
{% for version in versions|sort_version_aware %}
<option value="{{ version.slug }}">{{ version.slug }}</option>

View File

@ -16,7 +16,8 @@
{% endblocktrans %}
</p>
<form method="post" action="{% url "generic_build" project.pk %}">
<form method="post" action="{% url "projects_detail" project.slug %}">
{% csrf_token %}
<input type="hidden" name="version_slug" value="latest" />
<input type="submit" value="{% trans "Build latest version" %}" />
</form>