diff --git a/lib/Alchemy/Phrasea/Application.php b/lib/Alchemy/Phrasea/Application.php
index 8c7b2f58c2..5c3dfdffe0 100644
--- a/lib/Alchemy/Phrasea/Application.php
+++ b/lib/Alchemy/Phrasea/Application.php
@@ -72,6 +72,7 @@ use Alchemy\Phrasea\Controller\Utils\ConnectionTest;
use Alchemy\Phrasea\Controller\Utils\PathFileTest;
use Alchemy\Phrasea\Controller\User\Notifications;
use Alchemy\Phrasea\Controller\User\Preferences;
+use Alchemy\Phrasea\Core\Middleware\TokenMiddlewareProvider;
use Alchemy\Phrasea\Core\PhraseaExceptionHandler;
use Alchemy\Phrasea\Core\Event\Subscriber\LogoutSubscriber;
use Alchemy\Phrasea\Core\Event\Subscriber\PhraseaLocaleSubscriber;
@@ -211,6 +212,7 @@ class Application extends SilexApplication
}
$this->register(new BasketMiddlewareProvider());
+ $this->register(new TokenMiddlewareProvider());
$this->register(new ACLServiceProvider());
$this->register(new AuthenticationManagerServiceProvider());
diff --git a/lib/Alchemy/Phrasea/Authentication/Token/TokenValidator.php b/lib/Alchemy/Phrasea/Authentication/Token/TokenValidator.php
deleted file mode 100644
index 6a19d0f66f..0000000000
--- a/lib/Alchemy/Phrasea/Authentication/Token/TokenValidator.php
+++ /dev/null
@@ -1,43 +0,0 @@
-random = $random;
- }
-
- /**
- * Returns true if the token is valid
- *
- * @param type $token
- * @return boolean
- */
- public function isValid($token)
- {
- try {
- $datas = $this->random->helloToken($token);
-
- return $datas['usr_id'];
- } catch (NotFoundHttpException $e) {
-
- }
-
- return false;
- }
-}
diff --git a/lib/Alchemy/Phrasea/Command/Developer/RegenerateSqliteDb.php b/lib/Alchemy/Phrasea/Command/Developer/RegenerateSqliteDb.php
index a2318932c3..6da4cb6540 100644
--- a/lib/Alchemy/Phrasea/Command/Developer/RegenerateSqliteDb.php
+++ b/lib/Alchemy/Phrasea/Command/Developer/RegenerateSqliteDb.php
@@ -26,6 +26,7 @@ use Alchemy\Phrasea\Model\Entities\LazaretSession;
use Alchemy\Phrasea\Model\Entities\Registration;
use Alchemy\Phrasea\Model\Entities\Session;
use Alchemy\Phrasea\Model\Entities\Task;
+use Alchemy\Phrasea\Model\Entities\Token;
use Alchemy\Phrasea\Model\Entities\User;
use Alchemy\Phrasea\Model\Entities\ValidationData;
use Alchemy\Phrasea\Model\Entities\ValidationSession;
@@ -35,6 +36,7 @@ use Alchemy\Phrasea\Model\Entities\UsrList;
use Alchemy\Phrasea\Model\Entities\UsrListEntry;
use Alchemy\Phrasea\Model\Entities\StoryWZ;
use Alchemy\Phrasea\Core\Provider\ORMServiceProvider;
+use Alchemy\Phrasea\Model\Manipulator\TokenManipulator;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Tools\SchemaTool;
use Gedmo\Timestampable\TimestampableListener;
@@ -88,6 +90,8 @@ class RegenerateSqliteDb extends Command
$this->insertOauthApps($DI);
$this->generateCollection($DI);
$this->generateRecord($DI);
+ $this->insertTwoTasks($this->container['EM']);
+ $this->insertTwoBasket($this->container['EM'], $DI);
$this->insertOneStoryInWz($this->container['EM'], $DI);
$this->insertUsrLists($this->container['EM'], $DI);
$this->insertOnePrivateFeed($this->container['EM'], $DI);
@@ -99,7 +103,21 @@ class RegenerateSqliteDb extends Command
$this->insertOneRegistration($DI, $this->container['EM'], $DI['user_alt1'], $DI['coll'], 'now', 'registration_1');
$this->insertOneRegistration($DI, $this->container['EM'], $DI['user_alt2'], $DI['coll'], '-3 months', 'registration_2');
$this->insertOneRegistration($DI, $this->container['EM'], $DI['user_notAdmin'], $DI['coll'], 'now', 'registration_3');
+ $this->insertTwoTokens($this->container['EM'], $DI);
+ $this->insertOneInvalidToken($this->container['EM'], $DI);
+ $this->insertOneValidationToken($this->container['EM'], $DI);
+ $this->container['EM']->flush();
+
+ $fixtures['basket']['basket_1'] = $DI['basket_1']->getId();
+ $fixtures['basket']['basket_2'] = $DI['basket_2']->getId();
+ $fixtures['basket']['basket_3'] = $DI['basket_3']->getId();
+ $fixtures['basket']['basket_4'] = $DI['basket_4']->getId();
+
+ $fixtures['token']['token_1'] = $DI['token_1']->getValue();
+ $fixtures['token']['token_2'] = $DI['token_2']->getValue();
+ $fixtures['token']['token_invalid'] = $DI['token_invalid']->getValue();
+ $fixtures['token']['token_validation'] = $DI['token_validation']->getValue();
$fixtures['user']['test_phpunit'] = $DI['user']->getId();
$fixtures['user']['test_phpunit_not_admin'] = $DI['user_notAdmin']->getId();
$fixtures['user']['test_phpunit_alt1'] = $DI['user_alt1']->getId();
@@ -140,10 +158,13 @@ class RegenerateSqliteDb extends Command
$fixtures['user']['user_3_deleted'] = $DI['user_3_deleted']->getId();
$fixtures['user']['user_template'] = $DI['user_template']->getId();
- $this->insertTwoTasks($this->container['EM']);
- $this->insertTwoBasket($this->container['EM'], $DI);
+ $fixtures['feed']['public']['feed'] = $DI['feed_public']->getId();
+ $fixtures['feed']['public']['entry'] = $DI['feed_public_entry']->getId();
+ $fixtures['feed']['public']['token'] = $DI['feed_public_token']->getId();
- $this->container['EM']->flush();
+ $fixtures['feed']['private']['feed'] = $DI['feed_private']->getId();
+ $fixtures['feed']['private']['entry'] = $DI['feed_private_entry']->getId();
+ $fixtures['feed']['private']['token'] = $DI['feed_private_token']->getId();
} catch (\Exception $e) {
$output->writeln("".$e->getMessage()."");
if ($renamed) {
@@ -200,7 +221,6 @@ class RegenerateSqliteDb extends Command
$session = new LazaretSession();
$session->setUser($DI['user']);
$em->persist($session);
- $em->flush();
$file = File::buildFromPathfile($this->container['root.path'] . '/tests/files/cestlafete.jpg', $DI['coll'], $this->container);
@@ -248,8 +268,6 @@ class RegenerateSqliteDb extends Command
$em->persist($user2Deleted);
$em->persist($user3Deleted);
$em->persist($template);
-
- $em->flush();
}
protected function insertOneUser($login, $email = null, $admin = false)
@@ -385,6 +403,8 @@ class RegenerateSqliteDb extends Command
$basket1->setName('test');
$basket1->setDescription('description test');
+ $DI['basket_1'] = $basket1;
+
$element = new BasketElement();
$element->setRecord($DI['record_1']);
$basket1->addElement($element);
@@ -395,11 +415,15 @@ class RegenerateSqliteDb extends Command
$basket2->setName('test');
$basket2->setDescription('description test');
+ $DI['basket_2'] = $basket2;
+
$basket3 = new Basket();
$basket3->setUser($this->getUserAlt1());
$basket3->setName('test');
$basket3->setDescription('description test');
+ $DI['basket_3'] = $basket3;
+
$em->persist($basket1);
$em->persist($element);
$em->persist($basket2);
@@ -442,6 +466,8 @@ class RegenerateSqliteDb extends Command
$em->persist($validationParticipant);
}
+ $DI['basket_4'] = $basket4;
+
$em->persist($basket4);
}
@@ -524,8 +550,12 @@ class RegenerateSqliteDb extends Command
$em->persist($feed);
$em->persist($publisher);
- $this->insertOneFeedEntry($em, $DI, $feed, true);
- $this->insertOneFeedToken($em, $DI, $feed);
+ $entry = $this->insertOneFeedEntry($em, $DI, $feed, true);
+ $token = $this->insertOneFeedToken($em, $DI, $feed);
+
+ $DI['feed_public'] = $feed;
+ $DI['feed_public_entry'] = $entry;
+ $DI['feed_public_token'] = $token;
}
private function insertOnePrivateFeed(EntityManager $em, \Pimple $DI)
@@ -547,8 +577,12 @@ class RegenerateSqliteDb extends Command
$em->persist($feed);
$em->persist($publisher);
- $this->insertOneFeedEntry($em, $DI, $feed, false);
- $this->insertOneFeedToken($em, $DI, $feed);
+ $entry = $this->insertOneFeedEntry($em, $DI, $feed, false);
+ $token = $this->insertOneFeedToken($em, $DI, $feed);
+
+ $DI['feed_private'] = $feed;
+ $DI['feed_private_entry'] = $entry;
+ $DI['feed_private_token'] = $token;
}
private function insertOneExtraFeed(EntityManager $em, \Pimple $DI)
@@ -595,12 +629,14 @@ class RegenerateSqliteDb extends Command
$em->persist($feed);
$this->insertOneFeedItem($em, $DI, $entry, $public);
+
+ return $entry;
}
private function insertOneFeedToken(EntityManager $em, \Pimple $DI, Feed $feed)
{
$token = new FeedToken();
- $token->setValue($this->container['random.low']->generateString(64, \random::LETTERS_AND_NUMBERS));
+ $token->setValue($this->container['random.low']->generateString(64, TokenManipulator::LETTERS_AND_NUMBERS));
$token->setFeed($feed);
$token->setUser($DI['user']);
@@ -608,6 +644,8 @@ class RegenerateSqliteDb extends Command
$em->persist($token);
$em->persist($feed);
+
+ return $token;
}
private function insertOneAggregateToken(EntityManager $em, \Pimple $DI)
@@ -615,12 +653,61 @@ class RegenerateSqliteDb extends Command
$user = $DI['user'];
$token = new AggregateToken();
- $token->setValue($this->container['random.low']->generateString(64, \random::LETTERS_AND_NUMBERS));
+ $token->setValue($this->container['random.low']->generateString(64, TokenManipulator::LETTERS_AND_NUMBERS));
$token->setUser($user);
$em->persist($token);
}
+ private function insertTwoTokens(EntityManager $em, \Pimple $DI)
+ {
+ $user = $DI['user'];
+
+ $token = new Token();
+ $token->setValue($this->container['random.low']->generateString(12, TokenManipulator::LETTERS_AND_NUMBERS));
+ $token->setUser($user);
+ $token->setType(TokenManipulator::TYPE_RSS);
+ $token->setData('some data');
+ $DI['token_1'] = $token;
+ $em->persist($token);
+
+ $token = new Token();
+ $token->setValue($this->container['random.low']->generateString(12, TokenManipulator::LETTERS_AND_NUMBERS));
+ $token->setUser($user);
+ $token->setType(TokenManipulator::TYPE_RSS);
+ $token->setData('some data');
+ $token->setExpiration(new \DateTime('+1 year'));
+ $DI['token_2'] = $token;
+ $em->persist($token);
+ }
+
+ private function insertOneInvalidToken(EntityManager $em, \Pimple $DI)
+ {
+ $user = $DI['user'];
+
+ $token = new Token();
+ $token->setValue($this->container['random.low']->generateString(12, TokenManipulator::LETTERS_AND_NUMBERS));
+ $token->setUser($user);
+ $token->setType(TokenManipulator::TYPE_RSS);
+ $token->setData('some data');
+ $token->setExpiration(new \DateTime('-1 day'));
+ $DI['token_invalid'] = $token;
+ $em->persist($token);
+ }
+
+ private function insertOneValidationToken(EntityManager $em, \Pimple $DI)
+ {
+ $user = $DI['user'];
+
+ $token = new Token();
+ $token->setValue($this->container['random.low']->generateString(12, TokenManipulator::LETTERS_AND_NUMBERS));
+ $token->setUser($user);
+ $token->setType(TokenManipulator::TYPE_VALIDATE);
+ $token->setData($DI['basket_1']->getId());
+ $DI['token_validation'] = $token;
+ $em->persist($token);
+ }
+
private function insertOneFeedItem(EntityManager $em, \Pimple $DI, FeedEntry $entry, $public)
{
if ($public) {
@@ -656,7 +743,6 @@ class RegenerateSqliteDb extends Command
$registration->setUpdated(new \DateTime($when));
$registration->setCreated(new \DateTime($when));
$em->persist($registration);
- $em->flush();
$em->getEventManager()->addEventSubscriber(new TimestampableListener());
$DI[$name] = $registration;
diff --git a/lib/Alchemy/Phrasea/Controller/Lightbox.php b/lib/Alchemy/Phrasea/Controller/Lightbox.php
index ea3dda739a..f91d948e22 100644
--- a/lib/Alchemy/Phrasea/Controller/Lightbox.php
+++ b/lib/Alchemy/Phrasea/Controller/Lightbox.php
@@ -15,6 +15,7 @@ use Alchemy\Phrasea\Model\Entities\Basket;
use Alchemy\Phrasea\Model\Entities\BasketElement;
use Alchemy\Phrasea\Exception\SessionNotFound;
use Alchemy\Phrasea\Controller\Exception as ControllerException;
+use Alchemy\Phrasea\Model\Manipulator\TokenManipulator;
use Silex\ControllerProviderInterface;
use Silex\Application as SilexApplication;
use Symfony\Component\HttpFoundation\Request;
@@ -38,26 +39,21 @@ class Lightbox implements ControllerProviderInterface
$app['authentication']->closeAccount();
}
- if (false === $usr_id = $app['authentication.token-validator']->isValid($request->query->get('LOG'))) {
+ if (null === $token = $app['repo.tokens']->findValidToken($request->query->get('LOG'))) {
$app->addFlash('error', $app->trans('The URL you used is out of date, please login'));
return $app->redirectPath('homepage');
}
- $app['authentication']->openAccount($app['repo.users']->find($usr_id));
+ $app['authentication']->openAccount($token->getUser());
- try {
- $datas = $app['tokens']->helloToken($request->query->get('LOG'));
- } catch (NotFoundHttpException $e) {
- return;
- }
- switch ($datas['type']) {
- case \random::TYPE_FEED_ENTRY:
- return $app->redirectPath('lightbox_feed_entry', ['entry_id' => $datas['datas']]);
+ switch ($token->getType()) {
+ case TokenManipulator::TYPE_FEED_ENTRY:
+ return $app->redirectPath('lightbox_feed_entry', ['entry_id' => $token->getData()]);
break;
- case \random::TYPE_VALIDATE:
- case \random::TYPE_VIEW:
- return $app->redirectPath('lightbox_validation', ['basket' => $datas['datas']]);
+ case TokenManipulator::TYPE_VALIDATE:
+ case TokenManipulator::TYPE_VIEW:
+ return $app->redirectPath('lightbox_validation', ['basket' => $token->getData()]);
break;
}
});
@@ -464,13 +460,8 @@ class Lightbox implements ControllerProviderInterface
/* @var $basket Basket */
$participant = $basket->getValidation()->getParticipant($app['authentication']->getUser());
- $expires = new \DateTime('+10 days');
- $url = $app->url('lightbox', ['LOG' => $app['tokens']->getUrlToken(
- \random::TYPE_VALIDATE
- , $basket->getValidation()->getInitiator($app)->getId()
- , $expires
- , $basket->getId()
- )]);
+ $token = $app['manipulator.token']->createBasketValidationToken($basket);
+ $url = $app->url('lightbox', ['LOG' => $token->getValue()]);
$to = $basket->getValidation()->getInitiator($app)->getId();
$params = [
diff --git a/lib/Alchemy/Phrasea/Controller/Prod/DoDownload.php b/lib/Alchemy/Phrasea/Controller/Prod/DoDownload.php
index 5391c09e5d..b513c9183e 100644
--- a/lib/Alchemy/Phrasea/Controller/Prod/DoDownload.php
+++ b/lib/Alchemy/Phrasea/Controller/Prod/DoDownload.php
@@ -12,6 +12,7 @@
namespace Alchemy\Phrasea\Controller\Prod;
use Alchemy\Phrasea\Http\DeliverDataInterface;
+use Alchemy\Phrasea\Model\Entities\Token;
use Silex\Application;
use Silex\ControllerProviderInterface;
use Symfony\Component\HttpFoundation\Request;
@@ -32,16 +33,19 @@ class DoDownload implements ControllerProviderInterface
$controllers = $app['controllers_factory'];
$controllers->get('/{token}/prepare/', 'controller.prod.do-download:prepareDownload')
+ ->before($app['middleware.token.converter'])
->bind('prepare_download')
- ->assert('token', '[a-zA-Z0-9\.\/]{8,16}');
+ ->assert('token', '[a-zA-Z0-9]{8,32}');
$controllers->match('/{token}/get/', 'controller.prod.do-download:downloadDocuments')
+ ->before($app['middleware.token.converter'])
->bind('document_download')
- ->assert('token', '[a-zA-Z0-9\.\/]{8,16}');
+ ->assert('token', '[a-zA-Z0-9]{8,32}');
$controllers->post('/{token}/execute/', 'controller.prod.do-download:downloadExecute')
+ ->before($app['middleware.token.converter'])
->bind('execute_download')
- ->assert('token', '[a-zA-Z0-9\.\/]{8,16}');
+ ->assert('token', '[a-zA-Z0-9]{8,32}');
return $controllers;
}
@@ -51,15 +55,13 @@ class DoDownload implements ControllerProviderInterface
*
* @param Application $app
* @param Request $request
- * @param String $token
+ * @param Token $token
*
* @return Response
*/
- public function prepareDownload(Application $app, Request $request, $token)
+ public function prepareDownload(Application $app, Request $request, Token $token)
{
- $datas = $app['tokens']->helloToken($token);
-
- if (false === $list = @unserialize((string) $datas['datas'])) {
+ if (false === $list = @unserialize($token->getData())) {
$app->abort(500, 'Invalid datas');
}
@@ -96,15 +98,13 @@ class DoDownload implements ControllerProviderInterface
*
* @param Application $app
* @param Request $request
- * @param String $token
+ * @param Token $token
*
* @return Response
*/
- public function downloadDocuments(Application $app, Request $request, $token)
+ public function downloadDocuments(Application $app, Request $request, Token $token)
{
- $datas = $app['tokens']->helloToken($token);
-
- if (false === $list = @unserialize((string) $datas['datas'])) {
+ if (false === $list = @unserialize($token->getData())) {
$app->abort(500, 'Invalid datas');
}
@@ -118,7 +118,7 @@ class DoDownload implements ControllerProviderInterface
$mime = $subdef['mime'];
$list['complete'] = true;
} else {
- $exportFile = $app['root.path'] . '/tmp/download/' . $datas['value'] . '.zip';
+ $exportFile = $app['root.path'] . '/tmp/download/' . $token->getValue() . '.zip';
$mime = 'application/zip';
}
@@ -144,22 +144,13 @@ class DoDownload implements ControllerProviderInterface
*
* @param Application $app
* @param Request $request
- * @param String $token
+ * @param Token $token
*
* @return Response
*/
- public function downloadExecute(Application $app, Request $request, $token)
+ public function downloadExecute(Application $app, Request $request, Token $token)
{
- try {
- $datas = $app['tokens']->helloToken($token);
- } catch (NotFoundHttpException $e) {
- return $app->json([
- 'success' => false,
- 'message' => 'Invalid token'
- ]);
- }
-
- if (false === $list = @unserialize((string) $datas['datas'])) {
+ if (false === $list = @unserialize($token->getData())) {
return $app->json([
'success' => false,
'message' => 'Invalid datas'
@@ -175,7 +166,7 @@ class DoDownload implements ControllerProviderInterface
$app,
$token,
$list,
- sprintf($app['root.path'] . '/tmp/download/%s.zip', $datas['value']) // Dest file
+ sprintf($app['root.path'] . '/tmp/download/%s.zip', $token->getValue()) // Dest file
);
return $app->json([
diff --git a/lib/Alchemy/Phrasea/Controller/Prod/Download.php b/lib/Alchemy/Phrasea/Controller/Prod/Download.php
index 778491c79d..0a6ce8e57e 100644
--- a/lib/Alchemy/Phrasea/Controller/Prod/Download.php
+++ b/lib/Alchemy/Phrasea/Controller/Prod/Download.php
@@ -64,16 +64,7 @@ class Download implements ControllerProviderInterface
$list['export_name'] = sprintf('%s.zip', $download->getExportName());
- $token = $app['tokens']->getUrlToken(
- \random::TYPE_DOWNLOAD,
- $app['authentication']->getUser()->getId(),
- new \DateTime('+3 hours'), // Token lifetime
- serialize($list)
- );
-
- if (!$token) {
- throw new \RuntimeException('Download token could not be generated');
- }
+ $token = $app['manipulator.token']->createDownloadToken($app['authentication']->getUser(), serialize($list));
$app['events-manager']->trigger('__DOWNLOAD__', [
'lst' => $lst,
@@ -83,6 +74,6 @@ class Download implements ControllerProviderInterface
'export_file' => $download->getExportName()
]);
- return $app->redirectPath('prepare_download', ['token' => $token]);
+ return $app->redirectPath('prepare_download', ['token' => $token->getValue()]);
}
}
diff --git a/lib/Alchemy/Phrasea/Controller/Prod/Export.php b/lib/Alchemy/Phrasea/Controller/Prod/Export.php
index 51d617b63c..d97f8f47b0 100644
--- a/lib/Alchemy/Phrasea/Controller/Prod/Export.php
+++ b/lib/Alchemy/Phrasea/Controller/Prod/Export.php
@@ -215,22 +215,20 @@ class Export implements ControllerProviderInterface
}
}
- //generate validation token
- $endDateObject = new \DateTime('+1 day');
- $token = $app['tokens']->getUrlToken(\random::TYPE_EMAIL, false, $endDateObject, serialize($list));
+ $token = $app['manipulator.token']->createEmailExportToken(serialize($list));
- if (count($destMails) > 0 && $token) {
+ if (count($destMails) > 0) {
//zip documents
\set_export::build_zip(
$app,
$token,
$list,
- $app['root.path'] . '/tmp/download/' . $token . '.zip'
+ $app['root.path'] . '/tmp/download/' . $token->getValue() . '.zip'
);
$remaingEmails = $destMails;
- $url = $app->url('prepare_download', ['token' => $token, 'anonymous']);
+ $url = $app->url('prepare_download', ['token' => $token->getValue(), 'anonymous']);
$emitter = new Emitter($app['authentication']->getUser()->getDisplayName(), $app['authentication']->getUser()->getEmail());
@@ -243,7 +241,7 @@ class Export implements ControllerProviderInterface
$mail = MailRecordsExport::create($app, $receiver, $emitter, $request->request->get('textmail'));
$mail->setButtonUrl($url);
- $mail->setExpiration($endDateObject);
+ $mail->setExpiration($token->getExpiration());
$app['notification.deliverer']->deliver($mail);
unset($remaingEmails[$key]);
@@ -261,16 +259,6 @@ class Export implements ControllerProviderInterface
]);
}
}
- } elseif (!$token && count($destMails) > 0) { //couldn't generate token
- foreach ($destMails as $mail) {
- $app['events-manager']->trigger('__EXPORT_MAIL_FAIL__', [
- 'usr_id' => $app['authentication']->getUser()->getId(),
- 'lst' => $lst,
- 'ssttid' => $ssttid,
- 'dest' => $mail,
- 'reason' => 0
- ]);
- }
}
return $app->json([
diff --git a/lib/Alchemy/Phrasea/Controller/Prod/Push.php b/lib/Alchemy/Phrasea/Controller/Prod/Push.php
index 6e0e821c7f..a92379c7a4 100644
--- a/lib/Alchemy/Phrasea/Controller/Prod/Push.php
+++ b/lib/Alchemy/Phrasea/Controller/Prod/Push.php
@@ -219,12 +219,7 @@ class Push implements ControllerProviderInterface
$url = $app->url('lightbox_compare', [
'basket' => $Basket->getId(),
- 'LOG' => $app['tokens']->getUrlToken(
- \random::TYPE_VIEW,
- $user_receiver->getId(),
- null,
- $Basket->getId()
- )
+ 'LOG' => $app['manipulator.token']->createBasketAccessToken($Basket, $user_receiver),
]);
$receipt = $request->get('recept') ? $app['authentication']->getUser()->getEmail() : '';
@@ -414,12 +409,7 @@ class Push implements ControllerProviderInterface
$url = $app->url('lightbox_validation', [
'basket' => $Basket->getId(),
- 'LOG' => $app['tokens']->getUrlToken(
- \random::TYPE_VALIDATE,
- $participant_user->getId(),
- null,
- $Basket->getId()
- )
+ 'LOG' => $app['manipulator.token']->createBasketValidationToken($Basket, $participant_user),
]);
$receipt = $request->get('recept') ? $app['authentication']->getUser()->getEmail() : '';
diff --git a/lib/Alchemy/Phrasea/Controller/Root/Account.php b/lib/Alchemy/Phrasea/Controller/Root/Account.php
index a72b992caf..7a259eb660 100644
--- a/lib/Alchemy/Phrasea/Controller/Root/Account.php
+++ b/lib/Alchemy/Phrasea/Controller/Root/Account.php
@@ -138,9 +138,8 @@ class Account implements ControllerProviderInterface
return $app->redirectPath('account_reset_email');
}
- $date = new \DateTime('1 day');
- $token = $app['tokens']->getUrlToken(\random::TYPE_EMAIL, $app['authentication']->getUser()->getId(), $date, $app['authentication']->getUser()->getEmail());
- $url = $app->url('account_reset_email', ['token' => $token]);
+ $token = $app['manipulator.token']->createResetEmailToken($app['authentication']->getUser(), $email);
+ $url = $app->url('account_reset_email', ['token' => $token->getValue()]);
try {
$receiver = Receiver::fromUser($app['authentication']->getUser());
@@ -152,7 +151,7 @@ class Account implements ControllerProviderInterface
$mail = MailRequestEmailUpdate::create($app, $receiver, null);
$mail->setButtonUrl($url);
- $mail->setExpiration($date);
+ $mail->setExpiration($token->getExpiration());
$app['notification.deliverer']->deliver($mail);
@@ -170,21 +169,20 @@ class Account implements ControllerProviderInterface
*/
public function displayResetEmailForm(Application $app, Request $request)
{
- if (null !== $token = $request->query->get('token')) {
- try {
- $datas = $app['tokens']->helloToken($token);
- $user = $app['repo.users']->find((int) $datas['usr_id']);
- $user->setEmail($datas['datas']);
- $app['tokens']->removeToken($token);
-
- $app->addFlash('success', $app->trans('admin::compte-utilisateur: L\'email a correctement ete mis a jour'));
-
- return $app->redirectPath('account');
- } catch (\Exception $e) {
+ if (null !== $tokenValue = $request->query->get('token')) {
+ if (null === $token = $app['repo.tokens']->findValidToken($tokenValue)) {
$app->addFlash('error', $app->trans('admin::compte-utilisateur: erreur lors de la mise a jour'));
return $app->redirectPath('account');
}
+
+ $user = $token->getUser();
+ $user->setEmail($token->getData());
+ $app['manipulator.token']->delete($token);
+
+ $app->addFlash('success', $app->trans('admin::compte-utilisateur: L\'email a correctement ete mis a jour'));
+
+ return $app->redirectPath('account');
}
return $app['twig']->render('account/reset-email.html.twig', Login::getDefaultTemplateVariables($app));
diff --git a/lib/Alchemy/Phrasea/Controller/Root/Login.php b/lib/Alchemy/Phrasea/Controller/Root/Login.php
index 5f093a5290..546403921b 100644
--- a/lib/Alchemy/Phrasea/Controller/Root/Login.php
+++ b/lib/Alchemy/Phrasea/Controller/Root/Login.php
@@ -505,12 +505,11 @@ class Login implements ControllerProviderInterface
{
$receiver = Receiver::fromUser($user);
- $expire = new \DateTime('+3 days');
- $token = $app['tokens']->getUrlToken(\random::TYPE_PASSWORD, $user->getId(), $expire, $user->getEmail());
+ $token = $app['manipulator.token']->createAccountUnlockToken($user);
$mail = MailRequestEmailConfirmation::create($app, $receiver);
- $mail->setButtonUrl($app->url('login_register_confirm', ['code' => $token]));
- $mail->setExpiration($expire);
+ $mail->setButtonUrl($app->url('login_register_confirm', ['code' => $token->getValue()]));
+ $mail->setExpiration($token->getExpiration());
$app['notification.deliverer']->deliver($mail);
}
@@ -530,19 +529,13 @@ class Login implements ControllerProviderInterface
return $app->redirectPath('homepage');
}
- try {
- $datas = $app['tokens']->helloToken($code);
- } catch (NotFoundHttpException $e) {
+ if (null === $token = $app['repo.tokens']->findValidToken($code)) {
$app->addFlash('error', $app->trans('Invalid unlock link.'));
return $app->redirectPath('homepage');
}
- if (null === $user = $app['repo.users']->find((int) $datas['usr_id'])) {
- $app->addFlash('error', _('Invalid unlock link.'));
-
- return $app->redirectPath('homepage');
- }
+ $user = $token->getUser();
if (!$user->isMailLocked()) {
$app->addFlash('info', $app->trans('Account is already unlocked, you can login.'));
@@ -550,7 +543,7 @@ class Login implements ControllerProviderInterface
return $app->redirectPath('homepage');
}
- $app['tokens']->removeToken($code);
+ $app['manipulator.token']->delete($token);
$user->setMailLocked(false);
try {
@@ -561,7 +554,7 @@ class Login implements ControllerProviderInterface
return $app->redirectPath('homepage');
}
- $app['tokens']->removeToken($code);
+ $app['manipulator.token']->delete($token);
if (count($app['acl']->get($user)->get_granted_base()) > 0) {
$mail = MailSuccessEmailConfirmationRegistered::create($app, $receiver);
@@ -580,38 +573,26 @@ class Login implements ControllerProviderInterface
public function renewPassword(PhraseaApplication $app, Request $request)
{
- if (null === $token = $request->get('token')) {
+ if (null === $tokenValue = $request->get('token')) {
$app->abort(401, 'A token is required');
}
- try {
- $app['tokens']->helloToken($token);
- } catch (\Exception $e) {
+ if (null === $token = $app['repo.tokens']->findValidToken($tokenValue)) {
$app->abort(401, 'A token is required');
}
- $form = $app->form(new PhraseaRecoverPasswordForm($app['tokens']));
- $form->setData(['token' => $token]);
+ $form = $app->form(new PhraseaRecoverPasswordForm($app['repo.tokens']));
+ $form->setData(['token' => $token->getValue()]);
if ('POST' === $request->getMethod()) {
$form->bind($request);
- try {
- if ($form->isValid()) {
- $data = $form->getData();
+ if ($form->isValid()) {
+ $data = $form->getData();
+ $app['manipulator.user']->setPassword($token->getUser(), $data['password']);
+ $app['manipulator.token']->delete($token);
+ $app->addFlash('success', $app->trans('login::notification: Mise a jour du mot de passe avec succes'));
- $datas = $app['tokens']->helloToken($token);
-
- $user = $app['repo.users']->find($datas['usr_id']);
- $app['manipulator.user']->setPassword($user, $data['password']);
-
- $app['tokens']->removeToken($token);
-
- $app->addFlash('success', $app->trans('login::notification: Mise a jour du mot de passe avec succes'));
-
- return $app->redirectPath('homepage');
- }
- } catch (FormProcessingException $e) {
- $app->addFlash('error', $e->getMessage());
+ return $app->redirectPath('homepage');
}
}
@@ -649,13 +630,9 @@ class Login implements ControllerProviderInterface
throw new FormProcessingException($app->trans('Invalid email address'));
}
- $token = $app['tokens']->getUrlToken(\random::TYPE_PASSWORD, $user->getId(), new \DateTime('+1 day'));
+ $token = $app['manipulator.token']->createResetPasswordToken($user);
- if (!$token) {
- return $app->abort(500, 'Unable to generate a token');
- }
-
- $url = $app->url('login_renew_password', ['token' => $token], true);
+ $url = $app->url('login_renew_password', ['token' => $token->getValue()], true);
$mail = MailRequestPasswordUpdate::create($app, $receiver);
$mail->setLogin($user->getLogin());
@@ -837,20 +814,18 @@ class Login implements ControllerProviderInterface
$validationSession = $participant->getSession();
$participantId = $participant->getUser()->getId();
- $basketId = $validationSession->getBasket()->getId();
+ $basket = $validationSession->getBasket();
- try {
- $token = $app['tokens']->getValidationToken($participantId, $basketId);
- } catch (NotFoundHttpException $e) {
+ if (null === $token = $app['repo.tokens']->findValidationToken($basket, $participant->getUser())) {
continue;
}
$app['events-manager']->trigger('__VALIDATION_REMINDER__', [
'to' => $participantId,
- 'ssel_id' => $basketId,
+ 'ssel_id' => $basket->getId(),
'from' => $validationSession->getInitiator()->getId(),
'validate_id' => $validationSession->getId(),
- 'url' => $app->url('lightbox_validation', ['basket' => $basketId, 'LOG' => $token]),
+ 'url' => $app->url('lightbox_validation', ['basket' => $basket->getId(), 'LOG' => $token->getValue()]),
]);
$participant->setReminded(new \DateTime('now'));
diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/JsonRequestSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/JsonRequestSubscriber.php
index ef133cfca4..540c53e957 100644
--- a/lib/Alchemy/Phrasea/Core/Event/Subscriber/JsonRequestSubscriber.php
+++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/JsonRequestSubscriber.php
@@ -25,6 +25,7 @@ class JsonRequestSubscriber implements EventSubscriberInterface
if ((0 !== strpos($request->getPathInfo(), '/admin/')
|| 0 === strpos($request->getPathInfo(), '/admin/collection/')
+ || preg_match('/^\/download\/[a-zA-Z0-9]+\/execute\/$/', $request->getPathInfo())
|| 0 === strpos($request->getPathInfo(), '/admin/databox/'))
&& $request->getRequestFormat() == 'json') {
$datas = [
diff --git a/lib/Alchemy/Phrasea/Core/Middleware/TokenMiddlewareProvider.php b/lib/Alchemy/Phrasea/Core/Middleware/TokenMiddlewareProvider.php
new file mode 100644
index 0000000000..7502c9a086
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Core/Middleware/TokenMiddlewareProvider.php
@@ -0,0 +1,32 @@
+protect(function (Request $request, Application $app) {
+ if ($request->attributes->has('token')) {
+ $request->attributes->set('token', $app['converter.token']->convert($request->attributes->get('token')));
+ }
+ });
+ }
+
+ public function boot(Application $app)
+ {
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Core/Provider/AuthenticationManagerServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/AuthenticationManagerServiceProvider.php
index 925f2eb82b..a64948fa26 100644
--- a/lib/Alchemy/Phrasea/Core/Provider/AuthenticationManagerServiceProvider.php
+++ b/lib/Alchemy/Phrasea/Core/Provider/AuthenticationManagerServiceProvider.php
@@ -36,10 +36,6 @@ class AuthenticationManagerServiceProvider implements ServiceProviderInterface
return new Authenticator($app, $app['browser'], $app['session'], $app['EM']);
});
- $app['authentication.token-validator'] = $app->share(function (Application $app) {
- return new TokenValidator($app['tokens']);
- });
-
$app['authentication.persistent-manager'] = $app->share(function (Application $app) {
return new CookieManager($app['auth.password-encoder'], $app['repo.sessions'], $app['browser']);
});
diff --git a/lib/Alchemy/Phrasea/Core/Provider/ConfigurationTesterServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/ConfigurationTesterServiceProvider.php
index 7e80258cdb..9be12c08f9 100644
--- a/lib/Alchemy/Phrasea/Core/Provider/ConfigurationTesterServiceProvider.php
+++ b/lib/Alchemy/Phrasea/Core/Provider/ConfigurationTesterServiceProvider.php
@@ -15,6 +15,7 @@ use Alchemy\Phrasea\Setup\ConfigurationTester;
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Setup\Version\PreSchemaUpgrade\PreSchemaUpgradeCollection;
use Alchemy\Phrasea\Setup\Version\PreSchemaUpgrade\Upgrade39Feeds;
+use Alchemy\Phrasea\Setup\Version\PreSchemaUpgrade\Upgrade39Tokens;
use Alchemy\Phrasea\Setup\Version\PreSchemaUpgrade\Upgrade39Users;
use Silex\Application as SilexApplication;
use Silex\ServiceProviderInterface;
@@ -29,7 +30,7 @@ class ConfigurationTesterServiceProvider implements ServiceProviderInterface
});
$app['phraseanet.pre-schema-upgrader.upgrades'] = $app->share(function () {
- return [new Upgrade39Feeds(), new Upgrade39Users()];
+ return [new Upgrade39Feeds(), new Upgrade39Users(), new Upgrade39Tokens()];
});
$app['phraseanet.pre-schema-upgrader'] = $app->share(function (Application $app) {
diff --git a/lib/Alchemy/Phrasea/Core/Provider/ConvertersServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/ConvertersServiceProvider.php
index 82f42ac061..be04e45f92 100644
--- a/lib/Alchemy/Phrasea/Core/Provider/ConvertersServiceProvider.php
+++ b/lib/Alchemy/Phrasea/Core/Provider/ConvertersServiceProvider.php
@@ -13,6 +13,7 @@ namespace Alchemy\Phrasea\Core\Provider;
use Alchemy\Phrasea\Model\Converter\BasketConverter;
use Alchemy\Phrasea\Model\Converter\TaskConverter;
+use Alchemy\Phrasea\Model\Converter\TokenConverter;
use Silex\Application;
use Silex\ServiceProviderInterface;
@@ -27,6 +28,10 @@ class ConvertersServiceProvider implements ServiceProviderInterface
$app['converter.basket'] = $app->share(function ($app) {
return new BasketConverter($app['EM']);
});
+
+ $app['converter.token'] = $app->share(function ($app) {
+ return new TokenConverter($app['repo.tokens']);
+ });
}
public function boot(Application $app)
diff --git a/lib/Alchemy/Phrasea/Core/Provider/ManipulatorServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/ManipulatorServiceProvider.php
index 766d8a00b3..2ded0578b5 100644
--- a/lib/Alchemy/Phrasea/Core/Provider/ManipulatorServiceProvider.php
+++ b/lib/Alchemy/Phrasea/Core/Provider/ManipulatorServiceProvider.php
@@ -14,6 +14,7 @@ namespace Alchemy\Phrasea\Core\Provider;
use Alchemy\Phrasea\Model\Manipulator\ACLManipulator;
use Alchemy\Phrasea\Model\Manipulator\RegistrationManipulator;
use Alchemy\Phrasea\Model\Manipulator\TaskManipulator;
+use Alchemy\Phrasea\Model\Manipulator\TokenManipulator;
use Alchemy\Phrasea\Model\Manipulator\UserManipulator;
use Alchemy\Phrasea\Model\Manager\UserManager;
use Silex\Application as SilexApplication;
@@ -31,6 +32,10 @@ class ManipulatorServiceProvider implements ServiceProviderInterface
return new UserManipulator($app['model.user-manager'], $app['auth.password-encoder'], $app['geonames.connector'], $app['repo.users'], $app['random.low']);
});
+ $app['manipulator.token'] = $app->share(function ($app) {
+ return new TokenManipulator($app['EM'], $app['random.medium'], $app['repo.tokens']);
+ });
+
$app['manipulator.acl'] = $app->share(function ($app) {
return new ACLManipulator($app['acl'], $app['phraseanet.appbox']);
});
diff --git a/lib/Alchemy/Phrasea/Core/Provider/RepositoriesServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/RepositoriesServiceProvider.php
index 9220e53f11..02768a6400 100644
--- a/lib/Alchemy/Phrasea/Core/Provider/RepositoriesServiceProvider.php
+++ b/lib/Alchemy/Phrasea/Core/Provider/RepositoriesServiceProvider.php
@@ -91,6 +91,9 @@ class RepositoriesServiceProvider implements ServiceProviderInterface
$app['repo.user-queries'] = $app->share(function (PhraseaApplication $app) {
return $app['EM']->getRepository('Phraseanet:UserQuery');
});
+ $app['repo.tokens'] = $app->share(function ($app) {
+ return $app['EM']->getRepository('Phraseanet:Token');
+ });
}
public function boot(Application $app)
diff --git a/lib/Alchemy/Phrasea/Feed/Link/AggregateLinkGenerator.php b/lib/Alchemy/Phrasea/Feed/Link/AggregateLinkGenerator.php
index 6b09295bec..06d416f5c7 100644
--- a/lib/Alchemy/Phrasea/Feed/Link/AggregateLinkGenerator.php
+++ b/lib/Alchemy/Phrasea/Feed/Link/AggregateLinkGenerator.php
@@ -16,6 +16,7 @@ use Alchemy\Phrasea\Feed\Aggregate;
use Alchemy\Phrasea\Feed\FeedInterface;
use Alchemy\Phrasea\Model\Entities\AggregateToken;
use Alchemy\Phrasea\Model\Entities\User;
+use Alchemy\Phrasea\Model\Manipulator\TokenManipulator;
use Doctrine\ORM\EntityManager;
use RandomLib\Generator;
use Symfony\Component\Routing\Generator\UrlGenerator;
@@ -141,7 +142,7 @@ class AggregateLinkGenerator implements LinkGeneratorInterface
$token->setUser($user);
}
- $token->setValue($this->random->generateString(64, \random::LETTERS_AND_NUMBERS));
+ $token->setValue($this->random->generateString(64, TokenManipulator::LETTERS_AND_NUMBERS));
$this->em->persist($token);
$this->em->flush();
}
diff --git a/lib/Alchemy/Phrasea/Feed/Link/FeedLinkGenerator.php b/lib/Alchemy/Phrasea/Feed/Link/FeedLinkGenerator.php
index 4856112513..aeb7f631c2 100644
--- a/lib/Alchemy/Phrasea/Feed/Link/FeedLinkGenerator.php
+++ b/lib/Alchemy/Phrasea/Feed/Link/FeedLinkGenerator.php
@@ -14,6 +14,7 @@ namespace Alchemy\Phrasea\Feed\Link;
use Alchemy\Phrasea\Exception\InvalidArgumentException;
use Alchemy\Phrasea\Feed\FeedInterface;
use Alchemy\Phrasea\Model\Entities\User;
+use Alchemy\Phrasea\Model\Manipulator\TokenManipulator;
use Doctrine\ORM\EntityManager;
use Alchemy\Phrasea\Model\Entities\Feed;
use Alchemy\Phrasea\Model\Entities\FeedToken;
@@ -153,7 +154,7 @@ class FeedLinkGenerator implements LinkGeneratorInterface
$this->em->persist($feed);
}
- $token->setValue($this->random->generateString(64, \random::LETTERS_AND_NUMBERS));
+ $token->setValue($this->random->generateString(64, TokenManipulator::LETTERS_AND_NUMBERS));
$this->em->persist($token);
$this->em->flush();
}
diff --git a/lib/Alchemy/Phrasea/Form/Constraint/PasswordToken.php b/lib/Alchemy/Phrasea/Form/Constraint/PasswordToken.php
index 7edbdd8a5d..d117138aea 100644
--- a/lib/Alchemy/Phrasea/Form/Constraint/PasswordToken.php
+++ b/lib/Alchemy/Phrasea/Form/Constraint/PasswordToken.php
@@ -12,33 +12,33 @@
namespace Alchemy\Phrasea\Form\Constraint;
use Alchemy\Phrasea\Application;
+use Alchemy\Phrasea\Model\Manipulator\TokenManipulator;
+use Doctrine\ORM\EntityRepository;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class PasswordToken extends Constraint
{
public $message = 'The token provided is not valid anymore';
- private $random;
+ private $repository;
- public function __construct(\random $random)
+ public function __construct(EntityRepository $repository)
{
- $this->random = $random;
+ $this->repository = $repository;
parent::__construct();
}
- public function isValid($token)
+ public function isValid($tokenValue)
{
- try {
- $data = $this->random->helloToken($token);
- } catch (NotFoundHttpException $e) {
+ if (null === $token = $this->repository->findValidToken($tokenValue)) {
return false;
}
- return \random::TYPE_PASSWORD === $data['type'];
+ return TokenManipulator::TYPE_PASSWORD === $token->getType();
}
public static function create(Application $app)
{
- return new static($app['tokens']);
+ return new static($app['repo.tokens']);
}
}
diff --git a/lib/Alchemy/Phrasea/Form/Login/PhraseaRecoverPasswordForm.php b/lib/Alchemy/Phrasea/Form/Login/PhraseaRecoverPasswordForm.php
index e7f21225d1..7669a42ed7 100644
--- a/lib/Alchemy/Phrasea/Form/Login/PhraseaRecoverPasswordForm.php
+++ b/lib/Alchemy/Phrasea/Form/Login/PhraseaRecoverPasswordForm.php
@@ -12,6 +12,7 @@
namespace Alchemy\Phrasea\Form\Login;
use Alchemy\Phrasea\Form\Constraint\PasswordToken;
+use Doctrine\ORM\EntityRepository;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Constraints as Assert;
@@ -21,11 +22,11 @@ use Symfony\Component\Validator\Constraints as Assert;
*/
class PhraseaRecoverPasswordForm extends AbstractType
{
- private $tokens;
+ private $repository;
- public function __construct(\random $tokens)
+ public function __construct(EntityRepository $repository)
{
- $this->tokens = $tokens;
+ $this->repository = $repository;
}
public function buildForm(FormBuilderInterface $builder, array $options)
@@ -33,7 +34,7 @@ class PhraseaRecoverPasswordForm extends AbstractType
$builder->add('token', 'hidden', [
'required' => true,
'constraints' => [
- new PasswordToken($this->tokens)
+ new PasswordToken($this->repository)
]
]);
diff --git a/lib/Alchemy/Phrasea/Helper/User/Manage.php b/lib/Alchemy/Phrasea/Helper/User/Manage.php
index 03a24aa639..45916432b4 100644
--- a/lib/Alchemy/Phrasea/Helper/User/Manage.php
+++ b/lib/Alchemy/Phrasea/Helper/User/Manage.php
@@ -159,28 +159,22 @@ class Manage extends Helper
}
- if ($sendCredentials) {
- $urlToken = $this->app['tokens']->getUrlToken(\random::TYPE_PASSWORD, $createdUser->getId());
-
- if ($receiver && false !== $urlToken) {
- $url = $this->app->url('login_renew_password', ['token' => $urlToken]);
- $mail = MailRequestPasswordSetup::create($this->app, $receiver, null, '', $url);
- $mail->setLogin($createdUser->getLogin());
- $this->app['notification.deliverer']->deliver($mail);
- }
+ if ($sendCredentials && $receiver) {
+ $urlToken = $this->app['manipulator.token']->createResetPasswordToken($createdUser);
+ $url = $this->app->url('login_renew_password', ['token' => $urlToken->getValue()]);
+ $mail = MailRequestPasswordSetup::create($this->app, $receiver, null, '', $url);
+ $mail->setLogin($createdUser->getLogin());
+ $this->app['notification.deliverer']->deliver($mail);
}
- if ($validateMail) {
+ if ($validateMail && $receiver) {
$createdUser->setMailLocked(true);
- if ($receiver) {
- $expire = new \DateTime('+3 days');
- $token = $this->app['tokens']->getUrlToken(\random::TYPE_PASSWORD, $createdUser->getId(), $expire, $createdUser->getEmail());
- $url = $this->app->url('login_register_confirm', ['code' => $token]);
+ $token = $this->app['manipulator.token']->createAccountUnlockToken($createdUser);
+ $url = $this->app->url('login_register_confirm', ['code' => $token]);
- $mail = MailRequestEmailConfirmation::create($this->app, $receiver, null, '', $url, $expire);
- $this->app['notification.deliverer']->deliver($mail);
- }
+ $mail = MailRequestEmailConfirmation::create($this->app, $receiver, null, '', $url, $token->getExpiration());
+ $this->app['notification.deliverer']->deliver($mail);
}
}
diff --git a/lib/Alchemy/Phrasea/Model/Converter/TokenConverter.php b/lib/Alchemy/Phrasea/Model/Converter/TokenConverter.php
new file mode 100644
index 0000000000..c5e25313d1
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Model/Converter/TokenConverter.php
@@ -0,0 +1,40 @@
+repository = $repository;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return Token
+ */
+ public function convert($value)
+ {
+ if (null === $token = $this->repository->findValidToken($value)) {
+ throw new NotFoundHttpException('Token is not valid.');
+ }
+
+ return $token;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Model/Entities/Token.php b/lib/Alchemy/Phrasea/Model/Entities/Token.php
new file mode 100644
index 0000000000..2bdcd673f1
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Model/Entities/Token.php
@@ -0,0 +1,222 @@
+value = $value;
+
+ return $this;
+ }
+
+ /**
+ * Get value
+ *
+ * @return string
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+
+ /**
+ * Set type
+ *
+ * @param string $type
+ * @return Token
+ */
+ public function setType($type)
+ {
+ $this->type = $type;
+
+ return $this;
+ }
+
+ /**
+ * Get type
+ *
+ * @return string
+ */
+ public function getType()
+ {
+ return $this->type;
+ }
+
+ /**
+ * Set data
+ *
+ * @param string $data
+ * @return Token
+ */
+ public function setData($data)
+ {
+ $this->data = $data;
+
+ return $this;
+ }
+
+ /**
+ * Get data
+ *
+ * @return string
+ */
+ public function getData()
+ {
+ return $this->data;
+ }
+
+ /**
+ * Set created
+ *
+ * @param \DateTime $created
+ * @return Token
+ */
+ public function setCreated($created)
+ {
+ $this->created = $created;
+
+ return $this;
+ }
+
+ /**
+ * Get created
+ *
+ * @return \DateTime
+ */
+ public function getCreated()
+ {
+ return $this->created;
+ }
+
+ /**
+ * Set updated
+ *
+ * @param \DateTime $updated
+ * @return Token
+ */
+ public function setUpdated($updated)
+ {
+ $this->updated = $updated;
+
+ return $this;
+ }
+
+ /**
+ * Get updated
+ *
+ * @return \DateTime
+ */
+ public function getUpdated()
+ {
+ return $this->updated;
+ }
+
+ /**
+ * Set expiration
+ *
+ * @param \DateTime $updated
+ * @return Token
+ */
+ public function setExpiration(\DateTime $expiration = null)
+ {
+ $this->expiration = $expiration;
+
+ return $this;
+ }
+
+ /**
+ * Get expiration
+ *
+ * @return \DateTime
+ */
+ public function getExpiration()
+ {
+ return $this->expiration;
+ }
+
+ /**
+ * Set user
+ *
+ * @param User $user
+ * @return Token
+ */
+ public function setUser(User $user = null)
+ {
+ $this->user = $user;
+
+ return $this;
+ }
+
+ /**
+ * Get user
+ *
+ * @return User
+ */
+ public function getUser()
+ {
+ return $this->user;
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Model/Manager/UserManager.php b/lib/Alchemy/Phrasea/Model/Manager/UserManager.php
index ea83c62d67..731ac865a3 100644
--- a/lib/Alchemy/Phrasea/Model/Manager/UserManager.php
+++ b/lib/Alchemy/Phrasea/Model/Manager/UserManager.php
@@ -101,6 +101,16 @@ class UserManager
$user->getSettings()->clear();
}
+ private function cleanTokens(User $user)
+ {
+ $elements = $this->objectManager->getRepository('Phraseanet:Token')
+ ->findBy(['user' => $user]);
+
+ foreach ($elements as $element) {
+ $this->objectManager->remove($element);
+ }
+ }
+
/**
* Removes user queries.
*
@@ -194,16 +204,13 @@ class UserManager
*/
private function cleanProperties(User $user)
{
- foreach ([
- 'DELETE FROM `edit_presets` WHERE usr_id = :usr_id',
- 'DELETE FROM `tokens` WHERE usr_id = :usr_id',
- ] as $sql) {
- $stmt = $this->appboxConnection->prepare($sql);
- $stmt->execute([':usr_id' => $user->getId()]);
- $stmt->closeCursor();
- }
+ $sql = 'DELETE FROM `edit_presets` WHERE usr_id = :usr_id';
+ $stmt = $this->appboxConnection->prepare($sql);
+ $stmt->execute([':usr_id' => $user->getId()]);
+ $stmt->closeCursor();
$this->cleanSettings($user);
+ $this->cleanTokens($user);
$this->cleanQueries($user);
$this->cleanFtpCredentials($user);
$this->cleanOrders($user);
diff --git a/lib/Alchemy/Phrasea/Model/Manipulator/TokenManipulator.php b/lib/Alchemy/Phrasea/Model/Manipulator/TokenManipulator.php
new file mode 100644
index 0000000000..3d840ab3c7
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Model/Manipulator/TokenManipulator.php
@@ -0,0 +1,219 @@
+om = $om;
+ $this->random = $random;
+ $this->repository = $repository;
+ }
+
+ /**
+ * @param User|null $user
+ * @param string $type
+ * @param \DateTime|null $expiration
+ * @param mixed|null $data
+ *
+ * @return Token
+ */
+ public function create(User $user = null, $type, \DateTime $expiration = null, $data = null)
+ {
+ $this->removeExpiredTokens();
+
+ $n = 0;
+ do {
+ if ($n++ > 1024) {
+ throw new \RuntimeException('Unable to create a token.');
+ }
+ $value = $this->random->generateString(32, self::LETTERS_AND_NUMBERS);
+ $found = null !== $this->om->getRepository('Phraseanet:Token')->find($value);
+ } while ($found);
+
+ $token = new Token();
+
+ $token->setUser($user)
+ ->setType($type)
+ ->setValue($value)
+ ->setExpiration($expiration)
+ ->setData($data);
+
+ $this->om->persist($token);
+ $this->om->flush();
+
+ return $token;
+ }
+
+ /**
+ * @param Basket $basket
+ * @param User $user
+ *
+ * @return Token
+ */
+ public function createBasketValidationToken(Basket $basket, User $user = null)
+ {
+ if (null === $basket->getValidation()) {
+ throw new \InvalidArgumentException('A validation token requires a validation basket.');
+ }
+
+ return $this->create($user ?: $basket->getValidation()->getInitiator(), self::TYPE_VALIDATE, new \DateTime('+10 days'), $basket->getId());
+ }
+
+ /**
+ * @param Basket $basket
+ * @param User $user
+ *
+ * @return Token
+ */
+ public function createBasketAccessToken(Basket $basket, User $user)
+ {
+ return $this->create($user, self::TYPE_VIEW, null, $basket->getId());
+ }
+
+ /**
+ * @param User $user
+ * @param FeedEntry $entry
+ *
+ * @return Token
+ */
+ public function createFeedEntryToken(User $user, FeedEntry $entry)
+ {
+ return $this->create($user, self::TYPE_FEED_ENTRY, null, $entry->getId());
+ }
+
+ /**
+ * @param User $user
+ * @param $data
+ *
+ * @return Token
+ */
+ public function createDownloadToken(User $user, $data)
+ {
+ return $this->create($user, self::TYPE_DOWNLOAD, new \DateTime('+3 hours'), $data);
+ }
+
+ /**
+ * @param $data
+ *
+ * @return Token
+ */
+ public function createEmailExportToken($data)
+ {
+ return $this->create(null, self::TYPE_EMAIL, new \DateTime('+1 day'), $data);
+ }
+
+ /**
+ * @param User $user
+ * @param $email
+ *
+ * @return Token
+ */
+ public function createResetEmailToken(User $user, $email)
+ {
+ return $this->create($user, self::TYPE_EMAIL_RESET, new \DateTime('+1 day'), $email);
+ }
+
+ /**
+ * @param User $user
+ *
+ * @return Token
+ */
+ public function createAccountUnlockToken(User $user)
+ {
+ return $this->create($user, self::TYPE_ACCOUNT_UNLOCK, new \DateTime('+3 days'));
+ }
+
+ /**
+ * @param User $user
+ *
+ * @return Token
+ */
+ public function createResetPasswordToken(User $user)
+ {
+ return $this->create($user, self::TYPE_PASSWORD, new \DateTime('+1 day'));
+ }
+
+ /**
+ * Updates a token.
+ *
+ * @param Token $token
+ *
+ * @return Token
+ */
+ public function update(Token $token)
+ {
+ $this->om->persist($token);
+ $this->om->flush();
+
+ return $token;
+ }
+
+ /**
+ * Removes a token.
+ *
+ * @param Token $token
+ */
+ public function delete(Token $token)
+ {
+ $this->om->remove($token);
+ $this->om->flush();
+ }
+
+ /**
+ * Removes expired tokens
+ */
+ public function removeExpiredTokens()
+ {
+ foreach ($this->repository->findExpiredTokens() as $token) {
+ switch ($token->getType()) {
+ case 'download':
+ case 'email':
+ $file = $this->app['root.path'] . '/tmp/download/' . $token->getValue() . '.zip';
+ if (is_file($file)) {
+ unlink($file);
+ }
+ break;
+ }
+ $this->om->remove($token);
+ }
+ $this->om->flush();
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Model/Repositories/TokenRepository.php b/lib/Alchemy/Phrasea/Model/Repositories/TokenRepository.php
new file mode 100644
index 0000000000..020d9ae678
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Model/Repositories/TokenRepository.php
@@ -0,0 +1,59 @@
+ CURRENT_TIMESTAMP() OR t.expiration IS NULL)';
+
+ $query = $this->_em->createQuery($dql);
+ $query->setParameters([
+ ':type' => TokenManipulator::TYPE_VALIDATE,
+ ':user' => $user,
+ ':basket_id' => $basket->getId(),
+ ]);
+
+ return $query->getOneOrNullResult();
+ }
+
+ public function findValidToken($value)
+ {
+ $dql = 'SELECT t FROM Phraseanet:Token t
+ WHERE t.value = :value
+ AND (t.expiration IS NULL OR t.expiration >= CURRENT_TIMESTAMP())';
+
+ $query = $this->_em->createQuery($dql);
+ $query->setParameters([':value' => $value]);
+
+ return $query->getOneOrNullResult();
+ }
+
+ public function findExpiredTokens()
+ {
+ $dql = 'SELECT t FROM Phraseanet:Token t
+ WHERE t.expiration < :date';
+
+ $query = $this->_em->createQuery($dql);
+ $query->setParameters([':date' => new \DateTime()]);
+
+ return $query->getResult();
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Setup/DoctrineMigrations/TokenMigration.php b/lib/Alchemy/Phrasea/Setup/DoctrineMigrations/TokenMigration.php
new file mode 100644
index 0000000000..d7444e81dc
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Setup/DoctrineMigrations/TokenMigration.php
@@ -0,0 +1,34 @@
+abortIf($this->connection->getDatabasePlatform()->getName() != "mysql", "Migration can only be executed safely on 'mysql'.");
+
+ $this->addSql("CREATE TABLE Tokens (value VARCHAR(16) NOT NULL, user_id INT DEFAULT NULL, type VARCHAR(32) NOT NULL, data LONGTEXT DEFAULT NULL, created DATETIME NOT NULL, updated DATETIME NOT NULL, expiration DATETIME DEFAULT NULL, INDEX IDX_ADF614B8A76ED395 (user_id), PRIMARY KEY(value)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB");
+ $this->addSql("ALTER TABLE Tokens ADD CONSTRAINT FK_ADF614B8A76ED395 FOREIGN KEY (user_id) REFERENCES Users (id)");
+ }
+
+ public function doDownSql(Schema $schema)
+ {
+ // this down() migration is auto-generated, please modify it to your needs
+ $this->abortIf($this->connection->getDatabasePlatform()->getName() != "mysql", "Migration can only be executed safely on 'mysql'.");
+
+ $this->addSql("DROP TABLE Tokens");
+ }
+}
diff --git a/lib/Alchemy/Phrasea/Setup/Version/PreSchemaUpgrade/Upgrade39Feeds.php b/lib/Alchemy/Phrasea/Setup/Version/PreSchemaUpgrade/Upgrade39Feeds.php
index 5981201974..ab9e448d5e 100644
--- a/lib/Alchemy/Phrasea/Setup/Version/PreSchemaUpgrade/Upgrade39Feeds.php
+++ b/lib/Alchemy/Phrasea/Setup/Version/PreSchemaUpgrade/Upgrade39Feeds.php
@@ -41,7 +41,7 @@ class Upgrade39Feeds implements PreSchemaUpgradeInterface
public function rollback(EntityManager $em, \appbox $appbox, Configuration $conf)
{
if ($this->tableExists($em, 'feeds_backup')) {
- $em->getConnection()->executeUpdate('RENAME TABLE `feeds_backup` TO `feeds`');
+ $em->getConnection()->getSchemaManager()->renameTable('feeds_backup', 'feeds');
}
}
@@ -67,6 +67,6 @@ class Upgrade39Feeds implements PreSchemaUpgradeInterface
*/
private function doBackupFeedsTable(EntityManager $em)
{
- $em->getConnection()->executeUpdate('RENAME TABLE `feeds` TO `feeds_backup`');
+ $em->getConnection()->getSchemaManager()->renameTable('feeds', 'feeds_backup');
}
}
diff --git a/lib/Alchemy/Phrasea/Setup/Version/PreSchemaUpgrade/Upgrade39Tokens.php b/lib/Alchemy/Phrasea/Setup/Version/PreSchemaUpgrade/Upgrade39Tokens.php
new file mode 100644
index 0000000000..4055285616
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Setup/Version/PreSchemaUpgrade/Upgrade39Tokens.php
@@ -0,0 +1,75 @@
+doBackupFeedsTable($em);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isApplyable(Application $app)
+ {
+ return $this->tableExists($app['EM'], 'tokens');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function rollback(EntityManager $em, \appbox $appbox, Configuration $conf)
+ {
+ if ($this->tableExists($em, 'tokens_backup')) {
+ $em->getConnection()->executeUpdate('RENAME TABLE `tokens_backup` TO `tokens`');
+ }
+ }
+
+ /**
+ * Checks whether the table exists or not.
+ *
+ * @param $tableName
+ *
+ * @return boolean
+ */
+
+ private function tableExists(EntityManager $em, $table)
+ {
+ return (Boolean) $em->createNativeQuery(
+ 'SHOW TABLE STATUS WHERE Name="'.$table.'" COLLATE utf8_bin ', (new ResultSetMapping())->addScalarResult('Name', 'Name')
+ )->getOneOrNullResult();
+ }
+
+ /**
+ * Renames feed table.
+ *
+ * @param EntityManager $em
+ */
+ private function doBackupFeedsTable(EntityManager $em)
+ {
+ $em->getConnection()->executeUpdate('RENAME TABLE `tokens` TO `tokens_backup`');
+ }
+}
diff --git a/lib/classes/API/OAuth2/Application.php b/lib/classes/API/OAuth2/Application.php
index 7334221334..bb8dbc7cab 100644
--- a/lib/classes/API/OAuth2/Application.php
+++ b/lib/classes/API/OAuth2/Application.php
@@ -10,8 +10,9 @@
*/
use Alchemy\Phrasea\Application;
-use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Alchemy\Phrasea\Model\Entities\User;
+use Alchemy\Phrasea\Model\Manipulator\TokenManipulator;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class API_OAuth2_Application
{
@@ -606,8 +607,8 @@ class API_OAuth2_Application
)';
$nonce = $app['random.medium']->generateString(64);
- $client_secret = $app['random.medium']->generateString(32, \random::LETTERS_AND_NUMBERS);
- $client_token = $app['random.medium']->generateString(32, \random::LETTERS_AND_NUMBERS);
+ $client_secret = $app['random.medium']->generateString(32, TokenManipulator::LETTERS_AND_NUMBERS);
+ $client_token = $app['random.medium']->generateString(32, TokenManipulator::LETTERS_AND_NUMBERS);
$params = [
':usr_id' => $user ? $user->getId() : null,
diff --git a/lib/classes/API/OAuth2/Token.php b/lib/classes/API/OAuth2/Token.php
index 3bd21fe2fa..d85b5db509 100644
--- a/lib/classes/API/OAuth2/Token.php
+++ b/lib/classes/API/OAuth2/Token.php
@@ -10,6 +10,7 @@
*/
use Alchemy\Phrasea\Application;
+use Alchemy\Phrasea\Model\Manipulator\TokenManipulator;
use RandomLib\Generator;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@@ -230,7 +231,7 @@ class API_OAuth2_Token
$sql = 'UPDATE api_oauth_tokens SET oauth_token = :new_token
WHERE oauth_token = :old_token';
- $new_token = $this->generator->generateString(32, \random::LETTERS_AND_NUMBERS);
+ $new_token = $this->generator->generateString(32, TokenManipulator::LETTERS_AND_NUMBERS);
$params = [
':new_token' => $new_token
@@ -303,7 +304,7 @@ class API_OAuth2_Token
$expires = new \DateTime('+1 hour');
$params = [
- ':token' => $generator->generateString(32, \random::LETTERS_AND_NUMBERS)
+ ':token' => $generator->generateString(32, TokenManipulator::LETTERS_AND_NUMBERS)
, ':account_id' => $account->get_id()
, ':expire' => $expires->format(DATE_ISO8601)
, ':scope' => $scope
diff --git a/lib/classes/eventsmanager/notify/feed.php b/lib/classes/eventsmanager/notify/feed.php
index 88fc883560..c159b93a4d 100644
--- a/lib/classes/eventsmanager/notify/feed.php
+++ b/lib/classes/eventsmanager/notify/feed.php
@@ -88,14 +88,8 @@ class eventsmanager_notify_feed extends eventsmanager_notifyAbstract
if ($params['notify_email'] && $this->shouldSendNotificationFor($user_to_notif->getId())) {
$readyToSend = false;
try {
- $token = $this->app['tokens']->getUrlToken(
- \random::TYPE_FEED_ENTRY
- , $user_to_notif->getId()
- , null
- , $entry->getId()
- );
-
- $url = $this->app->url('lightbox', ['LOG' => $token]);
+ $token = $this->app['manipulator.token']->createFeedEntryToken($user_to_notif, $entry);
+ $url = $this->app->url('lightbox', ['LOG' => $token->getValue()]);
$receiver = Receiver::fromUser($user_to_notif);
$readyToSend = true;
diff --git a/lib/classes/eventsmanager/notify/orderdeliver.php b/lib/classes/eventsmanager/notify/orderdeliver.php
index be539d6004..f06861c581 100644
--- a/lib/classes/eventsmanager/notify/orderdeliver.php
+++ b/lib/classes/eventsmanager/notify/orderdeliver.php
@@ -109,12 +109,7 @@ class eventsmanager_notify_orderdeliver extends eventsmanager_notifyAbstract
if ($readyToSend) {
$url = $this->app->url('lightbox_compare', [
'basket' => $basket->getId(),
- 'LOG' => $this->app['tokens']->getUrlToken(
- \random::TYPE_VIEW,
- $user_to->getId(),
- null,
- $basket->getId()
- )
+ 'LOG' => $this->app['manipulator.token']->createBasketAccessToken($basket, $user_to)->getValue(),
]);
$mail = MailInfoOrderDelivered::create($this->app, $receiver, $emitter, null);
diff --git a/lib/classes/media/Permalink/Adapter.php b/lib/classes/media/Permalink/Adapter.php
index f6d4c30168..ba50f1adb9 100644
--- a/lib/classes/media/Permalink/Adapter.php
+++ b/lib/classes/media/Permalink/Adapter.php
@@ -11,8 +11,9 @@
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Exception\RuntimeException;
-use Guzzle\Http\Url;
+use Alchemy\Phrasea\Model\Manipulator\TokenManipulator;
use Doctrine\DBAL\DBALException;
+use Guzzle\Http\Url;
class media_Permalink_Adapter implements media_Permalink_Interface, cache_cacheableInterface
{
@@ -327,7 +328,7 @@ class media_Permalink_Adapter implements media_Permalink_Interface, cache_cachea
$params = [
':subdef_id' => $media_subdef->get_subdef_id()
- , ':token' => $app['random.medium']->generateString(64, \random::LETTERS_AND_NUMBERS)
+ , ':token' => $app['random.medium']->generateString(64, TokenManipulator::LETTERS_AND_NUMBERS)
, ':activated' => '1'
];
diff --git a/lib/classes/patch/390alpha14a.php b/lib/classes/patch/390alpha14a.php
index 958fe50228..84f0e9e824 100644
--- a/lib/classes/patch/390alpha14a.php
+++ b/lib/classes/patch/390alpha14a.php
@@ -58,7 +58,7 @@ class patch_390alpha14a extends patchAbstract
{
$app['conf']->remove(['main', 'api-timers']);
- if ($this->tableHasField($appbox, 'api_logs', 'api_log_ressource')) {
+ if ($this->tableHasField($app['EM'], 'api_logs', 'api_log_ressource')) {
$sql = 'UPDATE api_logs SET api_log_resource = api_log_ressource';
$app['phraseanet.appbox']->get_connection()->executeUpdate($sql);
}
diff --git a/lib/classes/patch/390alpha15a.php b/lib/classes/patch/390alpha15a.php
new file mode 100644
index 0000000000..3e58e25e18
--- /dev/null
+++ b/lib/classes/patch/390alpha15a.php
@@ -0,0 +1,70 @@
+release;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function require_all_upgrades()
+ {
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function concern()
+ {
+ return $this->concern;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getDoctrineMigrations()
+ {
+ return ['token'];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function apply(base $appbox, Application $app)
+ {
+ if (!$this->tableExists($app['EM'], 'tokens_backup')) {
+ return true;
+ }
+
+ $sql = 'INSERT INTO Tokens
+ (value, user_id, type, data, created, updated, expiration)
+ (SELECT value, usr_id, type, datas, created_on, created_on, expire_on FROM tokens_backup)';
+ $appbox->get_connection()->exec($sql);
+
+ return true;
+ }
+}
diff --git a/lib/classes/patchAbstract.php b/lib/classes/patchAbstract.php
index 70400ea56f..6fc7a3fdaf 100644
--- a/lib/classes/patchAbstract.php
+++ b/lib/classes/patchAbstract.php
@@ -11,6 +11,7 @@
use Doctrine\ORM\NoResultException;
use Doctrine\ORM\EntityManager;
+use Doctrine\ORM\Query\ResultSetMapping;
abstract class patchAbstract implements patchInterface
{
@@ -26,22 +27,21 @@ abstract class patchAbstract implements patchInterface
}
}
- protected function tableExists(base $base, $tableName)
+ protected function tableExists(EntityManager $em, $tableName)
{
- return $base
- ->get_connection()
- ->getSchemaManager()
- ->tablesExist([$tableName]);
+ return (Boolean) $em->createNativeQuery(
+ 'SHOW TABLE STATUS WHERE Name="'.$tableName.'" COLLATE utf8_bin ', (new ResultSetMapping())->addScalarResult('Name', 'Name')
+ )->getOneOrNullResult();
}
- protected function tableHasField(base $base, $tableName, $fieldName)
+ protected function tableHasField(EntityManager $em, $tableName, $fieldName)
{
- if (!$this->tableExists($base, $tableName)) {
+ if (!$this->tableExists($em, $tableName)) {
return false;
}
- return $base
- ->get_connection()
+ return $em
+ ->getConnection()
->getSchemaManager()
->listTableDetails($tableName)
->hasColumn($fieldName);
diff --git a/lib/classes/random.php b/lib/classes/random.php
index 02d1a997ab..07a0980a88 100644
--- a/lib/classes/random.php
+++ b/lib/classes/random.php
@@ -10,31 +10,9 @@
*/
use Alchemy\Phrasea\Application;
-use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class random
{
- /**
- *
- */
- const NUMBERS = "0123456789";
- /**
- *
- */
- const LETTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
- /**
- *
- */
- const LETTERS_AND_NUMBERS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
- const TYPE_FEED_ENTRY = 'FEED_ENTRY';
- const TYPE_PASSWORD = 'password';
- const TYPE_DOWNLOAD = 'download';
- const TYPE_MAIL_DOWNLOAD = 'mail-download';
- const TYPE_EMAIL = 'email';
- const TYPE_VIEW = 'view';
- const TYPE_VALIDATE = 'validate';
- const TYPE_RSS = 'rss';
-
private $app;
public function __construct(Application $app)
@@ -82,162 +60,4 @@ class random
return false;
}
-
- /**
- *
- * @param string $type
- * @param int $usr
- * @param DateTime $end_date
- * @param mixed content $datas
- *
- * @return boolean
- */
- public function getUrlToken($type, $usr, DateTime $end_date = null, $datas = '')
- {
- $this->cleanTokens();
- $conn = $this->app['phraseanet.appbox']->get_connection();
- $token = $test = false;
-
- switch ($type) {
- case self::TYPE_DOWNLOAD:
- case self::TYPE_MAIL_DOWNLOAD:
- case self::TYPE_EMAIL:
- case self::TYPE_PASSWORD:
- case self::TYPE_VALIDATE:
- case self::TYPE_VIEW:
- case self::TYPE_RSS:
- case self::TYPE_FEED_ENTRY:
- break;
- default:
- throw new Exception_InvalidArgument();
- break;
- }
-
- $n = 1;
-
- $sql = 'SELECT id FROM tokens WHERE value = :test ';
- $stmt = $conn->prepare($sql);
- while ($n < 100) {
- $test = $this->app['random.medium']->generateString(16, self::LETTERS_AND_NUMBERS);
- $stmt->execute([':test' => $test]);
- if ($stmt->rowCount() === 0) {
- $token = $test;
- break;
- }
- $n ++;
- }
- $stmt->closeCursor();
-
- if ($token) {
- $sql = 'INSERT INTO tokens (id, value, type, usr_id, created_on, expire_on, datas)
- VALUES (null, :token, :type, :usr, NOW(), :end_date, :datas)';
- $stmt = $conn->prepare($sql);
-
- $params = [
- ':token' => $token
- , ':type' => $type
- , ':usr' => ($usr ? $usr : '-1')
- , ':end_date' => ($end_date instanceof DateTime ? $end_date->format(DATE_ISO8601) : null)
- , ':datas' => ((trim($datas) != '') ? $datas : null)
- ];
- $stmt->execute($params);
- $stmt->closeCursor();
- }
-
- return $token;
- }
-
- public function removeToken($token)
- {
- $this->cleanTokens();
-
- try {
- $conn = $this->app['phraseanet.appbox']->get_connection();
- $sql = 'DELETE FROM tokens WHERE value = :token';
- $stmt = $conn->prepare($sql);
- $stmt->execute([':token' => $token]);
- $stmt->closeCursor();
-
- return true;
- } catch (\Exception $e) {
-
- }
-
- return false;
- }
-
- public function updateToken($token, $datas)
- {
- try {
- $conn = $this->app['phraseanet.appbox']->get_connection();
-
- $sql = 'UPDATE tokens SET datas = :datas
- WHERE value = :token';
-
- $stmt = $conn->prepare($sql);
- $stmt->execute([':datas' => $datas, ':token' => $token]);
- $stmt->closeCursor();
-
- return true;
- } catch (\Exception $e) {
-
- }
-
- return false;
- }
-
- public function helloToken($token)
- {
- $this->cleanTokens();
-
- $conn = $this->app['phraseanet.appbox']->get_connection();
- $sql = 'SELECT * FROM tokens
- WHERE value = :token
- AND (expire_on > NOW() OR expire_on IS NULL)';
- $stmt = $conn->prepare($sql);
- $stmt->execute([':token' => $token]);
- $row = $stmt->fetch(PDO::FETCH_ASSOC);
- $stmt->closeCursor();
-
- if ( ! $row)
- throw new NotFoundHttpException('Token not found');
-
- return $row;
- }
-
- /**
- * Get the validation token for one user and one validation basket
- *
- * @param integer $userId
- * @param integer $basketId
- *
- * @return string The token
- *
- * @throws NotFoundHttpException
- */
- public function getValidationToken($userId, $basketId)
- {
- $conn = $this->app['phraseanet.appbox']->get_connection();
- $sql = '
- SELECT value FROM tokens
- WHERE type = :type
- AND usr_id = :usr_id
- AND datas = :basket_id
- AND (expire_on > NOW() OR expire_on IS NULL)';
-
- $stmt = $conn->prepare($sql);
- $stmt->execute([
- ':type' => self::TYPE_VALIDATE,
- ':usr_id' => (int) $userId,
- ':basket_id' => (int) $basketId,
- ]);
- $row = $stmt->fetch(PDO::FETCH_ASSOC);
- $stmt->closeCursor();
-
- if (! $row) {
- throw new NotFoundHttpException('Token not found');
- }
-
- return $row['value'];
- }
}
diff --git a/lib/classes/set/export.php b/lib/classes/set/export.php
index a448caf3a6..4fd651d1fb 100644
--- a/lib/classes/set/export.php
+++ b/lib/classes/set/export.php
@@ -11,6 +11,7 @@
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Model\Serializer\CaptionSerializer;
+use Alchemy\Phrasea\Model\Entities\Token;
use Alchemy\Phrasea\Model\Entities\User;
use Symfony\Component\Filesystem\Filesystem;
@@ -693,7 +694,7 @@ class set_export extends set_abstract
*
* @return string
*/
- public static function build_zip(Application $app, $token, Array $list, $zipFile)
+ public static function build_zip(Application $app, Token $token, Array $list, $zipFile)
{
if (isset($list['complete']) && $list['complete'] === true) {
return;
@@ -703,7 +704,8 @@ class set_export extends set_abstract
$list['complete'] = false;
- $app['tokens']->updateToken($token, serialize($list));
+ $token->setData(serialize($list));
+ $app['manipulator.token']->update($token);
$toRemove = [];
$archiveFiles = [];
@@ -734,7 +736,8 @@ class set_export extends set_abstract
$list['complete'] = true;
- $app['tokens']->updateToken($token, serialize($list));
+ $token->setData(serialize($list));
+ $app['manipulator.token']->update($token);
$app['filesystem']->remove($toRemove);
$app['filesystem']->chmod($zipFile, 0760);
diff --git a/lib/conf.d/bases_structure.xml b/lib/conf.d/bases_structure.xml
index c8986bd03a..ecbe7f6013 100644
--- a/lib/conf.d/bases_structure.xml
+++ b/lib/conf.d/bases_structure.xml
@@ -2124,85 +2124,6 @@
InnoDB
-
-
-
- id
- int(11) unsigned
-
- auto_increment
-
-
- value
- char(16)
-
-
- ascii_bin
-
-
- type
- enum('FEED_ENTRY', 'view','validate','password','rss','email','download')
-
-
- ascii_bin
-
-
- usr_id
- int(11) unsigned
-
-
-
-
- datas
- longtext
- YES
-
-
-
- created_on
- datetime
-
-
-
-
- expire_on
- datetime
- YES
-
-
-
-
-
- PRIMARY
- PRIMARY
-
- id
-
-
-
- value
- UNIQUE
-
- value
-
-
-
- expire
- INDEX
-
- expire_on
-
-
-
- type
- INDEX
-
- type
-
-
-
- InnoDB
-
diff --git a/lib/conf.d/migrations.yml b/lib/conf.d/migrations.yml
index 9c5a00ea9e..c63fcd51a0 100644
--- a/lib/conf.d/migrations.yml
+++ b/lib/conf.d/migrations.yml
@@ -57,3 +57,6 @@ migrations:
migration18:
version: registration
class: Alchemy\Phrasea\Setup\DoctrineMigrations\RegistrationMigration
+ migration19:
+ version: token
+ class: Alchemy\Phrasea\Setup\DoctrineMigrations\TokenMigration
diff --git a/templates/web/prod/actions/Download/prepare.html.twig b/templates/web/prod/actions/Download/prepare.html.twig
index 5b8625857d..d435f48236 100644
--- a/templates/web/prod/actions/Download/prepare.html.twig
+++ b/templates/web/prod/actions/Download/prepare.html.twig
@@ -19,7 +19,7 @@
{% elseif list['complete'] is defined and list['complete'] %}
- {% set url = path('document_download', {'token': token}) %}
+ {% set url = path('document_download', {'token': token.getValue()}) %}
{% set before_link = '
' %}
{% set after_link = '' %}
{% trans with {'%before_link%' : before_link, '%after_link%' : after_link} %}Your documents are ready. If the download does not start, %before_link%click here%after_link%{% endtrans %}
@@ -91,7 +91,7 @@
-