1
0
Fork 0
mirror of https://github.com/wallabag/wallabag.git synced 2025-06-27 16:36:00 +00:00

Protect generate_token with a CSRF token

This commit is contained in:
Yassine Guedidi 2025-03-18 23:38:48 +01:00
parent f71d8332e0
commit d703fa6a3a
4 changed files with 72 additions and 63 deletions

View file

@ -38,3 +38,18 @@ nav .input-field input {
.tab {
flex: 1;
}
.btn-link {
background: none;
border: 0;
padding: 0;
color: $blue-accent-color;
&:focus {
background: none;
}
}
.inline-block {
display: inline-block;
}

View file

@ -16,6 +16,7 @@ use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Validator\Constraints\Locale as LocaleConstraint;
@ -429,22 +430,22 @@ class ConfigController extends AbstractController
}
/**
* @Route("/generate-token", name="generate_token")
* @Route("/generate-token", name="generate_token", methods={"POST"})
*
* @return RedirectResponse|JsonResponse
*/
public function generateTokenAction(Request $request)
{
if (!$this->isCsrfTokenValid('generate-token', $request->request->get('token'))) {
throw new BadRequestHttpException('Bad CSRF token.');
}
$config = $this->getConfig();
$config->setFeedToken(Utils::generateToken());
$this->entityManager->persist($config);
$this->entityManager->flush();
if ($request->isXmlHttpRequest()) {
return new JsonResponse(['token' => $config->getFeedToken()]);
}
$this->addFlash(
'notice',
'flashes.config.notice.feed_token_updated'

View file

@ -123,48 +123,58 @@
</div>
<div id="set2" class="col s12">
<div class="row">
<div class="input-field col s12">
{{ 'config.form_feed.description'|trans }}
</div>
</div>
<div class="row">
<div class="col s12">
<h6 class="grey-text">{{ 'config.form_feed.token_label'|trans }}</h6>
<div>
{% if feed.token %}
{{ feed.token }}
{% else %}
<em>{{ 'config.form_feed.no_token'|trans }}</em>
{% endif %}
{% if feed.token %}
<form action="{{ path('generate_token') }}" method="post" class="inline-block">
<input type="hidden" name="token" value="{{ csrf_token('generate-token') }}"/>
<button type="submit" class="btn-link">{{ 'config.form_feed.token_reset'|trans }}</button>
</form>
<a href="{{ path('revoke_token') }}">{{ 'config.form_feed.token_revoke'|trans }}</a>
{% else %}
<form action="{{ path('generate_token') }}" method="post" class="inline-block">
<input type="hidden" name="token" value="{{ csrf_token('generate-token') }}"/>
<button type="submit" class="btn-link">{{ 'config.form_feed.token_create'|trans }}</button>
</form>
{% endif %}
</div>
</div>
</div>
{% if feed.token %}
<div class="row">
<div class="col s12">
<h6 class="grey-text">{{ 'config.form_feed.feed_links'|trans }}</h6>
<ul>
<li><a href="{{ path('unread_feed', {'username': feed.username, 'token': feed.token}) }}">{{ 'config.form_feed.feed_link.unread'|trans }}</a></li>
<li><a href="{{ path('starred_feed', {'username': feed.username, 'token': feed.token}) }}">{{ 'config.form_feed.feed_link.starred'|trans }}</a></li>
<li><a href="{{ path('archive_feed', {'username': feed.username, 'token': feed.token}) }}">{{ 'config.form_feed.feed_link.archive'|trans }}</a></li>
<li><a href="{{ path('all_feed', {'username': feed.username, 'token': feed.token}) }}">{{ 'config.form_feed.feed_link.all'|trans }}</a></li>
</ul>
</div>
</div>
{% endif %}
{{ form_start(form.feed) }}
{{ form_errors(form.feed) }}
<div class="row">
<div class="input-field col s12">
{{ 'config.form_feed.description'|trans }}
</div>
</div>
<div class="row">
<div class="col s12">
<h6 class="grey-text">{{ 'config.form_feed.token_label'|trans }}</h6>
<div>
{% if feed.token %}
{{ feed.token }}
{% else %}
<em>{{ 'config.form_feed.no_token'|trans }}</em>
{% endif %}
{% if feed.token %}
<a href="{{ path('generate_token') }}">{{ 'config.form_feed.token_reset'|trans }}</a>
<a href="{{ path('revoke_token') }}">{{ 'config.form_feed.token_revoke'|trans }}</a>
{% else %}
<a href="{{ path('generate_token') }}">{{ 'config.form_feed.token_create'|trans }}</a>
{% endif %}
</div>
</div>
</div>
{% if feed.token %}
<div class="row">
<div class="col s12">
<h6 class="grey-text">{{ 'config.form_feed.feed_links'|trans }}</h6>
<ul>
<li><a href="{{ path('unread_feed', {'username': feed.username, 'token': feed.token}) }}">{{ 'config.form_feed.feed_link.unread'|trans }}</a></li>
<li><a href="{{ path('starred_feed', {'username': feed.username, 'token': feed.token}) }}">{{ 'config.form_feed.feed_link.starred'|trans }}</a></li>
<li><a href="{{ path('archive_feed', {'username': feed.username, 'token': feed.token}) }}">{{ 'config.form_feed.feed_link.archive'|trans }}</a></li>
<li><a href="{{ path('all_feed', {'username': feed.username, 'token': feed.token}) }}">{{ 'config.form_feed.feed_link.all'|trans }}</a></li>
</ul>
</div>
</div>
{% endif %}
<div class="row">
<div class="input-field col s12">
{{ form_label(form.feed.feed_limit) }}

View file

@ -328,7 +328,8 @@ class ConfigControllerTest extends WallabagCoreTestCase
$this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
$this->assertStringContainsString('config.form_feed.no_token', $body[0]);
$client->request('GET', '/generate-token');
$client->submit($crawler->selectButton('config.form_feed.token_create')->form());
$this->assertSame(302, $client->getResponse()->getStatusCode());
$crawler = $client->followRedirect();
@ -337,24 +338,6 @@ class ConfigControllerTest extends WallabagCoreTestCase
$this->assertStringContainsString('config.form_feed.token_reset', $body[0]);
}
public function testGenerateTokenAjax()
{
$this->logInAs('admin');
$client = $this->getTestClient();
$client->request(
'GET',
'/generate-token',
[],
[],
['HTTP_X-Requested-With' => 'XMLHttpRequest']
);
$this->assertSame(200, $client->getResponse()->getStatusCode());
$content = json_decode($client->getResponse()->getContent(), true);
$this->assertArrayHasKey('token', $content);
}
public function testRevokeTokenAjax()
{
$this->logInAs('admin');