From 2daa6bc58be1e6a524571458c5413608b0f007ee Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Guillaume=20Perr=C3=A9al?= <guillaume.perreal@irstea.fr>
Date: Mon, 26 Jan 2015 16:00:44 +0100
Subject: [PATCH] =?UTF-8?q?Utilise=20un=20token=20CSRF=20unique=20pour=20t?=
 =?UTF-8?q?outes=20les=20op=C3=A9rations=20sur=20les=20fichiers.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 Controller/UploadController.php            | 44 ++++------------------
 Form/Type/FileUploadType.php               |  4 +-
 Resources/js/widget/file_upload.js         | 13 ++++---
 Resources/views/Form/file_upload.html.twig |  3 +-
 4 files changed, 20 insertions(+), 44 deletions(-)

diff --git a/Controller/UploadController.php b/Controller/UploadController.php
index 18589565..cfcc654a 100644
--- a/Controller/UploadController.php
+++ b/Controller/UploadController.php
@@ -28,8 +28,7 @@ use Symfony\Component\Routing\RouterInterface;
  */
 class UploadController extends Controller
 {
-    const CSRF_READ_INTENTION = "uploaded_file_read";
-    const CSRF_WRITE_INTENTION = "uploaded_file_write_%s";
+    const CSRF_INTENTION = "uploaded_file";
 
     /**
      *
@@ -74,12 +73,7 @@ class UploadController extends Controller
             isset($data['lastModified']) ? $data['lastModified'] : null
         );
 
-        $token = $this->generateCsrfToken(self::CSRF_WRITE_INTENTION, $file);
-
-        $parameters = [
-            'id' => $file->getId(),
-            'token' => $token
-        ];
+        $parameters = ['id' => $file->getId()];
 
         $deleteUrl = $this->router->generate('file_upload_delete', $parameters);
 
@@ -105,7 +99,7 @@ class UploadController extends Controller
      */
     public function putContentAction(Request $request, UploadedFile $file)
     {
-        $this->validateCsrfToken($request, self::CSRF_WRITE_INTENTION, $file);
+        $this->validateCsrfToken($request);
 
         if(null !== $range = $request->headers->get('Content-Range', null)) {
             $matches = [];
@@ -141,10 +135,7 @@ class UploadController extends Controller
 
         $this->fileManager->completed($file);
 
-        $parameters = [
-            'id'    => $file->getId(),
-            'token' => $request->query->get('token')
-        ];
+        $parameters = ['id' => $file->getId()];
 
         return $this->createResponse(
             Response::HTTP_OK,
@@ -174,7 +165,7 @@ class UploadController extends Controller
      */
     public function getContentAction(Request $request, UploadedFile $file)
     {
-        $this->validateCsrfToken($request, self::CSRF_READ_INTENTION);
+        $this->validateCsrfToken($request);
 
         if(!$file->isValid()) {
             throw new NotFoundHttpException();
@@ -193,40 +184,21 @@ class UploadController extends Controller
      */
     public function deleteAction(Request $request, UploadedFile $file)
     {
-        $this->validateCsrfToken($request, self::CSRF_WRITE_INTENTION, $file);
+        $this->validateCsrfToken($request);
 
         $this->fileManager->delete($file);
 
         return $this->createResponse();
     }
 
-    /**
-     *
-     * @param type $intention
-     * @param UploadedFile $file
-     * @return string
-     */
-    protected function generateCsrfToken($intention, UploadedFile $file = null)
-    {
-        if(null !== $file) {
-            $intention = sprintf($intention, $file->getId());
-        }
-        return $this->csrfProvider->generateCsrfToken($intention);
-    }
-
     /**
      *
      * @param Request $request
-     * @param type $intention
-     * @param UploadedFile $file
      * @throws HttpException
      */
-    protected function validateCsrfToken(Request $request, $intention, UploadedFile $file = null)
+    protected function validateCsrfToken(Request $request)
     {
-        if(null !== $file) {
-            $intention = sprintf($intention, $file->getId());
-        }
-        if(!$this->csrfProvider->isCsrfTokenValid($intention, $request->query->get('token', null))) {
+        if(!$this->csrfProvider->isCsrfTokenValid(self::CSRF_INTENTION, $request->query->get('token', null))) {
             throw new HttpException(Response::HTTP_FORBIDDEN, 'Invalid CSRF token');
         }
     }
diff --git a/Form/Type/FileUploadType.php b/Form/Type/FileUploadType.php
index 82fd1ae4..6539fde1 100644
--- a/Form/Type/FileUploadType.php
+++ b/Form/Type/FileUploadType.php
@@ -53,7 +53,7 @@ class FileUploadType extends AbstractType
     {
         $this->router = $router;
         $this->fileManager = $fileManager;
-        $this->csrfToken = $csrfProvider->generateCsrfToken(UploadController::CSRF_READ_INTENTION);
+        $this->csrfToken = $csrfProvider->generateCsrfToken(UploadController::CSRF_INTENTION);
         $this->maxChunkSize = $maxChunkSize;
     }
 
@@ -74,7 +74,7 @@ class FileUploadType extends AbstractType
         $this->buildWidgetView($view, $form, $options);
 
         $view->vars['widget_attr']['data-create-url'] = $this->router->generate('file_upload_create');
-        $view->vars['csrfToken'] = $this->csrfToken;
+        $view->vars['csrf_token'] = $this->csrfToken;
         $view->vars['multiple'] = $options['multiple'];
         if($options['max_chunk_size'] > 0) {
             $view->vars['widget_attr']['data-max-chunk-size'] = $options['max_chunk_size'];
diff --git a/Resources/js/widget/file_upload.js b/Resources/js/widget/file_upload.js
index 0b7ee4c4..dc68e3f7 100644
--- a/Resources/js/widget/file_upload.js
+++ b/Resources/js/widget/file_upload.js
@@ -32,11 +32,13 @@
             $entries          = $this.find('.fileinput-entries'),
             createUrl         = options.createUrl,
             uploadPrototype   = options.uploadPrototype,
-            downloadPrototype = options.downloadPrototype;
+            downloadPrototype = options.downloadPrototype,
+            csrfQuery         = '?token=' + options.csrfToken;
 
         delete options.createUrl;
         delete options.uploadPrototype;
         delete options.downloadPrototype;
+        delete options.csrfToken;
 
         if(options.acceptFileTypes) {
             options.acceptFileTypes = new RegExp('^' + options.acceptFileTypes + '$');
@@ -95,10 +97,10 @@
                         } else {
                             row.find('.name a')
                                 .text(file.name)
-                                .prop('href', file.url);
                             row.find('button.delete')
+                                .prop('href', file.url + csrfQuery);
                                 .attr('data-type', file.delete_type)
-                                .attr('data-url', file.delete_url);
+                                .attr('data-url', file.delete_url + csrfQuery);
                             row.find('input:hidden')
                                 .val(file.id);
                         }
@@ -114,7 +116,8 @@
                         { file: { name: file.name, size: file.size, type: file.type, lastModified: file.lastModified } },
                         function(response) {
                             if(response.status == 201) {
-                                data.url = response.put_url;
+                                file.icon = response.icon;
+                                data.url = response.put_url + csrfQuery;
                                 data.delete_url = response.delete_url;
                                 data.delete_type = response.delete_type;
                                 data.jqXHR = $this.fileupload('send', data);
@@ -144,7 +147,7 @@
         )).bind({
             fileuploadfailed: function (e, data) {
                 if(data.delete_url) {
-                    $.ajax(data.delete_url, { type: data.delete_type });
+                    $.ajax(data.delete_url + csrfQuery, { type: data.delete_type });
                 }
             },
             fileuploadadded: updateDisplay,
diff --git a/Resources/views/Form/file_upload.html.twig b/Resources/views/Form/file_upload.html.twig
index dcfea721..c4faca4a 100644
--- a/Resources/views/Form/file_upload.html.twig
+++ b/Resources/views/Form/file_upload.html.twig
@@ -18,7 +18,7 @@
     <div class="template-download fileinput-entry">
         <input type="hidden" name="{{ full_name }}{% if multiple %}[]{% endif %}" value="{{ file.id|default }}"/>
         <span class="name">
-            <a{% if file %} href="{{ path('file_upload_get_content', {id: file.id, token: csrfToken}) }}"{% endif %}>{{ file.originalFilename|default }}</a>
+            <a{% if file %} href="{{ path('file_upload_get_content', {id: file.id, token: csrf_token}) }}"{% endif %}>{{ file.originalFilename|default }}</a>
         </span>
         (<span class="size">
             {{- file.size|default -}}
@@ -34,6 +34,7 @@
     <div id="{{ id }}" class="form-control fileinput-{{ multiple ? "multiple" : "single" }}" {{ block('widget_container_attributes') }} {{ block('widget_only_attributes') }}
          data-download-prototype="{{ block('file_upload_entry_prototype')|e }}"
          data-upload-prototype="{{ block('file_upload_progress_prototype')|e }}"
+         data-csrf-token="{{ csrf_token }}"
          {% if disabled %}data-disabled="disabled"{% endif -%}
          {% if read_only %}data-readonly="readonly"{% endif -%}
          >
-- 
GitLab