Commit 2daa6bc5 authored by Guillaume Perréal's avatar Guillaume Perréal
Browse files

Utilise un token CSRF unique pour toutes les opérations sur les fichiers.

Showing with 20 additions and 44 deletions
+20 -44
...@@ -28,8 +28,7 @@ use Symfony\Component\Routing\RouterInterface; ...@@ -28,8 +28,7 @@ use Symfony\Component\Routing\RouterInterface;
*/ */
class UploadController extends Controller class UploadController extends Controller
{ {
const CSRF_READ_INTENTION = "uploaded_file_read"; const CSRF_INTENTION = "uploaded_file";
const CSRF_WRITE_INTENTION = "uploaded_file_write_%s";
/** /**
* *
...@@ -74,12 +73,7 @@ class UploadController extends Controller ...@@ -74,12 +73,7 @@ class UploadController extends Controller
isset($data['lastModified']) ? $data['lastModified'] : null isset($data['lastModified']) ? $data['lastModified'] : null
); );
$token = $this->generateCsrfToken(self::CSRF_WRITE_INTENTION, $file); $parameters = ['id' => $file->getId()];
$parameters = [
'id' => $file->getId(),
'token' => $token
];
$deleteUrl = $this->router->generate('file_upload_delete', $parameters); $deleteUrl = $this->router->generate('file_upload_delete', $parameters);
...@@ -105,7 +99,7 @@ class UploadController extends Controller ...@@ -105,7 +99,7 @@ class UploadController extends Controller
*/ */
public function putContentAction(Request $request, UploadedFile $file) 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)) { if(null !== $range = $request->headers->get('Content-Range', null)) {
$matches = []; $matches = [];
...@@ -141,10 +135,7 @@ class UploadController extends Controller ...@@ -141,10 +135,7 @@ class UploadController extends Controller
$this->fileManager->completed($file); $this->fileManager->completed($file);
$parameters = [ $parameters = ['id' => $file->getId()];
'id' => $file->getId(),
'token' => $request->query->get('token')
];
return $this->createResponse( return $this->createResponse(
Response::HTTP_OK, Response::HTTP_OK,
...@@ -174,7 +165,7 @@ class UploadController extends Controller ...@@ -174,7 +165,7 @@ class UploadController extends Controller
*/ */
public function getContentAction(Request $request, UploadedFile $file) public function getContentAction(Request $request, UploadedFile $file)
{ {
$this->validateCsrfToken($request, self::CSRF_READ_INTENTION); $this->validateCsrfToken($request);
if(!$file->isValid()) { if(!$file->isValid()) {
throw new NotFoundHttpException(); throw new NotFoundHttpException();
...@@ -193,40 +184,21 @@ class UploadController extends Controller ...@@ -193,40 +184,21 @@ class UploadController extends Controller
*/ */
public function deleteAction(Request $request, UploadedFile $file) public function deleteAction(Request $request, UploadedFile $file)
{ {
$this->validateCsrfToken($request, self::CSRF_WRITE_INTENTION, $file); $this->validateCsrfToken($request);
$this->fileManager->delete($file); $this->fileManager->delete($file);
return $this->createResponse(); 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 Request $request
* @param type $intention
* @param UploadedFile $file
* @throws HttpException * @throws HttpException
*/ */
protected function validateCsrfToken(Request $request, $intention, UploadedFile $file = null) protected function validateCsrfToken(Request $request)
{ {
if(null !== $file) { if(!$this->csrfProvider->isCsrfTokenValid(self::CSRF_INTENTION, $request->query->get('token', null))) {
$intention = sprintf($intention, $file->getId());
}
if(!$this->csrfProvider->isCsrfTokenValid($intention, $request->query->get('token', null))) {
throw new HttpException(Response::HTTP_FORBIDDEN, 'Invalid CSRF token'); throw new HttpException(Response::HTTP_FORBIDDEN, 'Invalid CSRF token');
} }
} }
......
...@@ -53,7 +53,7 @@ class FileUploadType extends AbstractType ...@@ -53,7 +53,7 @@ class FileUploadType extends AbstractType
{ {
$this->router = $router; $this->router = $router;
$this->fileManager = $fileManager; $this->fileManager = $fileManager;
$this->csrfToken = $csrfProvider->generateCsrfToken(UploadController::CSRF_READ_INTENTION); $this->csrfToken = $csrfProvider->generateCsrfToken(UploadController::CSRF_INTENTION);
$this->maxChunkSize = $maxChunkSize; $this->maxChunkSize = $maxChunkSize;
} }
...@@ -74,7 +74,7 @@ class FileUploadType extends AbstractType ...@@ -74,7 +74,7 @@ class FileUploadType extends AbstractType
$this->buildWidgetView($view, $form, $options); $this->buildWidgetView($view, $form, $options);
$view->vars['widget_attr']['data-create-url'] = $this->router->generate('file_upload_create'); $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']; $view->vars['multiple'] = $options['multiple'];
if($options['max_chunk_size'] > 0) { if($options['max_chunk_size'] > 0) {
$view->vars['widget_attr']['data-max-chunk-size'] = $options['max_chunk_size']; $view->vars['widget_attr']['data-max-chunk-size'] = $options['max_chunk_size'];
......
...@@ -32,11 +32,13 @@ ...@@ -32,11 +32,13 @@
$entries = $this.find('.fileinput-entries'), $entries = $this.find('.fileinput-entries'),
createUrl = options.createUrl, createUrl = options.createUrl,
uploadPrototype = options.uploadPrototype, uploadPrototype = options.uploadPrototype,
downloadPrototype = options.downloadPrototype; downloadPrototype = options.downloadPrototype,
csrfQuery = '?token=' + options.csrfToken;
delete options.createUrl; delete options.createUrl;
delete options.uploadPrototype; delete options.uploadPrototype;
delete options.downloadPrototype; delete options.downloadPrototype;
delete options.csrfToken;
if(options.acceptFileTypes) { if(options.acceptFileTypes) {
options.acceptFileTypes = new RegExp('^' + options.acceptFileTypes + '$'); options.acceptFileTypes = new RegExp('^' + options.acceptFileTypes + '$');
...@@ -95,10 +97,10 @@ ...@@ -95,10 +97,10 @@
} else { } else {
row.find('.name a') row.find('.name a')
.text(file.name) .text(file.name)
.prop('href', file.url);
row.find('button.delete') row.find('button.delete')
.prop('href', file.url + csrfQuery);
.attr('data-type', file.delete_type) .attr('data-type', file.delete_type)
.attr('data-url', file.delete_url); .attr('data-url', file.delete_url + csrfQuery);
row.find('input:hidden') row.find('input:hidden')
.val(file.id); .val(file.id);
} }
...@@ -114,7 +116,8 @@ ...@@ -114,7 +116,8 @@
{ file: { name: file.name, size: file.size, type: file.type, lastModified: file.lastModified } }, { file: { name: file.name, size: file.size, type: file.type, lastModified: file.lastModified } },
function(response) { function(response) {
if(response.status == 201) { 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_url = response.delete_url;
data.delete_type = response.delete_type; data.delete_type = response.delete_type;
data.jqXHR = $this.fileupload('send', data); data.jqXHR = $this.fileupload('send', data);
...@@ -144,7 +147,7 @@ ...@@ -144,7 +147,7 @@
)).bind({ )).bind({
fileuploadfailed: function (e, data) { fileuploadfailed: function (e, data) {
if(data.delete_url) { if(data.delete_url) {
$.ajax(data.delete_url, { type: data.delete_type }); $.ajax(data.delete_url + csrfQuery, { type: data.delete_type });
} }
}, },
fileuploadadded: updateDisplay, fileuploadadded: updateDisplay,
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
<div class="template-download fileinput-entry"> <div class="template-download fileinput-entry">
<input type="hidden" name="{{ full_name }}{% if multiple %}[]{% endif %}" value="{{ file.id|default }}"/> <input type="hidden" name="{{ full_name }}{% if multiple %}[]{% endif %}" value="{{ file.id|default }}"/>
<span class="name"> <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>
(<span class="size"> (<span class="size">
{{- file.size|default -}} {{- file.size|default -}}
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
<div id="{{ id }}" class="form-control fileinput-{{ multiple ? "multiple" : "single" }}" {{ block('widget_container_attributes') }} {{ block('widget_only_attributes') }} <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-download-prototype="{{ block('file_upload_entry_prototype')|e }}"
data-upload-prototype="{{ block('file_upload_progress_prototype')|e }}" data-upload-prototype="{{ block('file_upload_progress_prototype')|e }}"
data-csrf-token="{{ csrf_token }}"
{% if disabled %}data-disabled="disabled"{% endif -%} {% if disabled %}data-disabled="disabled"{% endif -%}
{% if read_only %}data-readonly="readonly"{% endif -%} {% if read_only %}data-readonly="readonly"{% endif -%}
> >
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment