Skip to content

Commit

Permalink
Merge pull request #6851 from mautic-inc/staging.delete-expired-user-…
Browse files Browse the repository at this point in the history
…tokens

Delete expired user tokens with maintenance cleanup command
  • Loading branch information
Woeler committed Nov 22, 2018
2 parents d16cc2d + a17353d commit a2e4c2d
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 5 deletions.
4 changes: 3 additions & 1 deletion app/bundles/CoreBundle/Config/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,12 @@
'mautic.core.model.auditlog',
],
],

'mautic.core.maintenance.subscriber' => [
'class' => 'Mautic\CoreBundle\EventListener\MaintenanceSubscriber',
'class' => Mautic\CoreBundle\EventListener\MaintenanceSubscriber::class,
'arguments' => [
'doctrine.dbal.default_connection',
'mautic.user.token.repository',
],
],
'mautic.core.request.subscriber' => [
Expand Down
19 changes: 15 additions & 4 deletions app/bundles/CoreBundle/EventListener/MaintenanceSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Doctrine\DBAL\Connection;
use Mautic\CoreBundle\CoreEvents;
use Mautic\CoreBundle\Event\MaintenanceEvent;
use Mautic\UserBundle\Entity\UserTokenRepositoryInterface;

/**
* Class MaintenanceSubscriber.
Expand All @@ -25,14 +26,21 @@ class MaintenanceSubscriber extends CommonSubscriber
*/
protected $db;

/**
* @var UserTokenRepositoryInterface
*/
private $userTokenRepository;

/**
* MaintenanceSubscriber constructor.
*
* @param Connection $db
* @param Connection $db
* @param UserTokenRepositoryInterface $userTokenRepository
*/
public function __construct(Connection $db)
public function __construct(Connection $db, UserTokenRepositoryInterface $userTokenRepository)
{
$this->db = $db;
$this->db = $db;
$this->userTokenRepository = $userTokenRepository;
}

/**
Expand All @@ -52,11 +60,14 @@ public function onDataCleanup(MaintenanceEvent $event)
{
$this->cleanupData($event, 'audit_log');
$this->cleanupData($event, 'notifications');

$rows = $this->userTokenRepository->deleteExpired($event->isDryRun());
$event->setStat($this->translator->trans('mautic.maintenance.user_tokens'), $rows);
}

/**
* @param MaintenanceEvent $event
* @param $table
* @param string $table
*/
private function cleanupData(MaintenanceEvent $event, $table)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<?php

/*
* @copyright 2018 Mautic Contributors. All rights reserved
* @author Mautic
*
* @link http://mautic.org
*
* @license GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
*/

namespace Mautic\CoreBundle\Test\EventListener;

use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Query\Expression\ExpressionBuilder;
use Doctrine\DBAL\Query\QueryBuilder;
use Mautic\CoreBundle\CoreEvents;
use Mautic\CoreBundle\Event\MaintenanceEvent;
use Mautic\CoreBundle\EventListener\MaintenanceSubscriber;
use Mautic\UserBundle\Entity\UserTokenRepositoryInterface;
use Symfony\Component\Translation\TranslatorInterface;

class MaintenanceSubscriberTest extends \PHPUnit_Framework_TestCase
{
/**
* @var MaintenanceSubscriber
*/
private $subscriber;

public function setUp()
{
$connection = $this->createMock(Connection::class);
$userTokenRepository = $this->createMock(UserTokenRepositoryInterface::class);
$this->subscriber = new MaintenanceSubscriber($connection, $userTokenRepository);
$translator = $this->createMock(TranslatorInterface::class);
$this->subscriber->setTranslator($translator);
}

public function testGetSubscribedEvents()
{
$this->assertEquals(
[CoreEvents::MAINTENANCE_CLEANUP_DATA => ['onDataCleanup', -50]],
$this->subscriber->getSubscribedEvents()
);
}

public function testOnDataCleanup()
{
if (!defined('MAUTIC_TABLE_PREFIX')) {
define('MAUTIC_TABLE_PREFIX', 'mautic');
}

$dateTime = new \DateTimeImmutable();
$format = 'Y-m-d H:i:s';
$rowCount = 2;
$translatedString = 'nonsense';

$dateTimeMock = $this->createMock(\DateTime::class);
$dateTimeMock
->expects($this->exactly(2))
->method('format')
->with($format)
->willReturn($dateTime->format($format));

$event = $this->createMock(MaintenanceEvent::class);
$event
->expects($this->exactly(2))
->method('getDate')
->willReturn($dateTimeMock);
$event
->expects($this->exactly(3))
->method('isDryRun')
->willReturn(false);
$event
->expects($this->exactly(3))
->method('setStat');

$expressionBuilder = $this->createMock(ExpressionBuilder::class);
$expressionBuilder
->expects($this->exactly(2))
->method('lte')
->with('date_added', ':date');

$qb = $this->createMock(QueryBuilder::class);
$qb
->expects($this->exactly(2))
->method('setParameter')
->willReturn($qb);
$qb
->expects($this->exactly(2))
->method('delete')
->willReturn($qb);
$qb
->expects($this->exactly(2))
->method('expr')
->willReturn($expressionBuilder);
$qb
->expects($this->exactly(2))
->method('where')
->willReturn($qb);
$qb
->expects($this->exactly(2))
->method('execute')
->willReturn($rowCount);

$connection = $this->createMock(Connection::class);
$connection
->expects($this->exactly(2))
->method('createQueryBuilder')
->willReturn($qb);

$userTokenRepository = $this->createMock(UserTokenRepositoryInterface::class);
$subscriber = new MaintenanceSubscriber($connection, $userTokenRepository);

$translator = $this->createMock(TranslatorInterface::class);
$translator
->expects($this->exactly(3))
->method('trans')
->willReturn($translatedString);
$subscriber->setTranslator($translator);

$this->assertNull($subscriber->onDataCleanup($event));
}
}
1 change: 1 addition & 0 deletions app/bundles/CoreBundle/Translations/en_US/messages.ini
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,7 @@ mautic.core.theme.feature.email="Email"
mautic.core.theme.feature.form="Form"
mautic.maintenance.audit_log="Audit log entries"
mautic.maintenance.notifications="User notifications"
mautic.maintenance.user_tokens="User tokens"
mautic.maintenance.confirm_data_purge="This will permanently delete data older than %days% days! Please make a backup before proceeding. Run this command with --dry-run to get approximate records to be deleted. Continue? (y/n)"
mautic.maintenance.header.key="Record type"
mautic.maintenance.header.records_affected="Records affected"
Expand Down
5 changes: 5 additions & 0 deletions app/bundles/UserBundle/Config/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,11 @@
'arguments' => 'Mautic\UserBundle\Entity\User',
'factory' => ['@mautic.user.manager', 'getRepository'],
],
'mautic.user.token.repository' => [
'class' => 'Mautic\UserBundle\Entity\UserTokenRepository',
'arguments' => 'Mautic\UserBundle\Entity\UserToken',
'factory' => ['@doctrine', 'getRepository'],
],
'mautic.permission.manager' => [
'class' => 'Doctrine\ORM\EntityManager',
'arguments' => 'Mautic\UserBundle\Entity\Permission',
Expand Down
22 changes: 22 additions & 0 deletions app/bundles/UserBundle/Entity/UserTokenRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,26 @@ public function verify(UserToken $token)

return true;
}

/**
* {@inheritdoc}
*/
public function deleteExpired($isDryRun = false)
{
$qb = $this->createQueryBuilder('ut');

if ($isDryRun) {
$qb->select('count(ut.id) as records');
} else {
$qb->delete(UserToken::class, 'ut');
}

$resultCount = (int) $qb
->where('ut.expiration <= :current_datetime')
->setParameter(':current_datetime', new \DateTime())
->getQuery()
->execute();

return $resultCount;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,13 @@ public function isSecretUnique($secret);
* @return bool
*/
public function verify(UserToken $token);

/**
* Delete expired user tokens.
*
* @param bool $isDryRun
*
* @return int Number of selected or deleted rows
*/
public function deleteExpired($isDryRun = false);
}

0 comments on commit a2e4c2d

Please sign in to comment.