mirror of
https://github.com/wallabag/wallabag.git
synced 2025-08-06 17:41:01 +00:00
Protect generate_token with a CSRF token
This commit is contained in:
parent
f71d8332e0
commit
d703fa6a3a
4 changed files with 72 additions and 63 deletions
|
@ -38,3 +38,18 @@ nav .input-field input {
|
||||||
.tab {
|
.tab {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-link {
|
||||||
|
background: none;
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
color: $blue-accent-color;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-block {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||||
use Symfony\Component\Routing\Annotation\Route;
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||||
use Symfony\Component\Validator\Constraints\Locale as LocaleConstraint;
|
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
|
* @return RedirectResponse|JsonResponse
|
||||||
*/
|
*/
|
||||||
public function generateTokenAction(Request $request)
|
public function generateTokenAction(Request $request)
|
||||||
{
|
{
|
||||||
|
if (!$this->isCsrfTokenValid('generate-token', $request->request->get('token'))) {
|
||||||
|
throw new BadRequestHttpException('Bad CSRF token.');
|
||||||
|
}
|
||||||
|
|
||||||
$config = $this->getConfig();
|
$config = $this->getConfig();
|
||||||
$config->setFeedToken(Utils::generateToken());
|
$config->setFeedToken(Utils::generateToken());
|
||||||
|
|
||||||
$this->entityManager->persist($config);
|
$this->entityManager->persist($config);
|
||||||
$this->entityManager->flush();
|
$this->entityManager->flush();
|
||||||
|
|
||||||
if ($request->isXmlHttpRequest()) {
|
|
||||||
return new JsonResponse(['token' => $config->getFeedToken()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->addFlash(
|
$this->addFlash(
|
||||||
'notice',
|
'notice',
|
||||||
'flashes.config.notice.feed_token_updated'
|
'flashes.config.notice.feed_token_updated'
|
||||||
|
|
|
@ -123,48 +123,58 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="set2" class="col s12">
|
<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_start(form.feed) }}
|
||||||
{{ form_errors(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="row">
|
||||||
<div class="input-field col s12">
|
<div class="input-field col s12">
|
||||||
{{ form_label(form.feed.feed_limit) }}
|
{{ form_label(form.feed.feed_limit) }}
|
||||||
|
|
|
@ -328,7 +328,8 @@ class ConfigControllerTest extends WallabagCoreTestCase
|
||||||
$this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
|
$this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
|
||||||
$this->assertStringContainsString('config.form_feed.no_token', $body[0]);
|
$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());
|
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
||||||
|
|
||||||
$crawler = $client->followRedirect();
|
$crawler = $client->followRedirect();
|
||||||
|
@ -337,24 +338,6 @@ class ConfigControllerTest extends WallabagCoreTestCase
|
||||||
$this->assertStringContainsString('config.form_feed.token_reset', $body[0]);
|
$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()
|
public function testRevokeTokenAjax()
|
||||||
{
|
{
|
||||||
$this->logInAs('admin');
|
$this->logInAs('admin');
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue