diff options
author | Thomas Citharel <tcit@tcit.fr> | 2017-05-28 14:53:04 +0200 |
---|---|---|
committer | Thomas Citharel <tcit@tcit.fr> | 2017-05-28 14:53:04 +0200 |
commit | d5fbb570c974fe8b6f64356772f7cd60b96419da (patch) | |
tree | c4cfdd0459473d28707b373b53ddee3cc32ac844 | |
parent | 35941d57ee4d06ec3557d4b126d5f6fd263bcf3a (diff) | |
download | wallabag-d5fbb570c974fe8b6f64356772f7cd60b96419da.tar.gz wallabag-d5fbb570c974fe8b6f64356772f7cd60b96419da.tar.zst wallabag-d5fbb570c974fe8b6f64356772f7cd60b96419da.zip |
Hash the urls to check if they exist
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
6 files changed, 150 insertions, 20 deletions
diff --git a/src/Wallabag/ApiBundle/Controller/EntryRestController.php b/src/Wallabag/ApiBundle/Controller/EntryRestController.php index 4801811d..2b07cb59 100644 --- a/src/Wallabag/ApiBundle/Controller/EntryRestController.php +++ b/src/Wallabag/ApiBundle/Controller/EntryRestController.php | |||
@@ -21,8 +21,8 @@ class EntryRestController extends WallabagRestController | |||
21 | * | 21 | * |
22 | * @ApiDoc( | 22 | * @ApiDoc( |
23 | * parameters={ | 23 | * parameters={ |
24 | * {"name"="url", "dataType"="string", "required"=true, "format"="An url", "description"="Url to check if it exists"}, | 24 | * {"name"="hashedurl", "dataType"="string", "required"=true, "format"="An url", "description"="SHA512 Url to check if it exists"}, |
25 | * {"name"="urls", "dataType"="string", "required"=false, "format"="An array of urls (?urls[]=http...&urls[]=http...)", "description"="Urls (as an array) to check if it exists"} | 25 | * {"name"="hashedurls", "dataType"="string", "required"=false, "format"="An array of urls (?urls[]=http...&urls[]=http...)", "description"="SHA512 Urls (as an array) to check if it exists"} |
26 | * } | 26 | * } |
27 | * ) | 27 | * ) |
28 | * | 28 | * |
@@ -32,33 +32,38 @@ class EntryRestController extends WallabagRestController | |||
32 | { | 32 | { |
33 | $this->validateAuthentication(); | 33 | $this->validateAuthentication(); |
34 | 34 | ||
35 | $urls = $request->query->get('urls', []); | 35 | $hashedUrls = $request->query->get('hashedurls', []); |
36 | 36 | ||
37 | // handle multiple urls first | 37 | // handle multiple urls first |
38 | if (!empty($urls)) { | 38 | if (!empty($hashedUrls)) { |
39 | $results = []; | 39 | $results = []; |
40 | foreach ($urls as $url) { | 40 | foreach ($hashedUrls as $hashedUrl) { |
41 | $res = $this->getDoctrine() | 41 | $res = $this->getDoctrine() |
42 | ->getRepository('WallabagCoreBundle:Entry') | 42 | ->getRepository('WallabagCoreBundle:Entry') |
43 | ->findByUrlAndUserId($url, $this->getUser()->getId()); | 43 | ->findOneBy([ |
44 | 'hashedUrl' => $hashedUrl, | ||
45 | 'user' => $this->getUser()->getId(), | ||
46 | ]); | ||
44 | 47 | ||
45 | $results[$url] = $res instanceof Entry ? $res->getId() : false; | 48 | $results[$hashedUrl] = $res instanceof Entry ? $res->getId() : false; |
46 | } | 49 | } |
47 | 50 | ||
48 | return $this->sendResponse($results); | 51 | return $this->sendResponse($results); |
49 | } | 52 | } |
50 | 53 | ||
51 | // let's see if it is a simple url? | 54 | // let's see if it is a simple url? |
52 | $url = $request->query->get('url', ''); | 55 | $hashedUrl = $request->query->get('hashedurl', ''); |
53 | 56 | ||
54 | if (empty($url)) { | 57 | if (empty($hashedUrl)) { |
55 | throw $this->createAccessDeniedException('URL is empty?, logged user id: '.$this->getUser()->getId()); | 58 | throw $this->createAccessDeniedException('URL is empty?, logged user id: '.$this->getUser()->getId()); |
56 | } | 59 | } |
57 | 60 | ||
58 | $res = $this->getDoctrine() | 61 | $res = $this->getDoctrine() |
59 | ->getRepository('WallabagCoreBundle:Entry') | 62 | ->getRepository('WallabagCoreBundle:Entry') |
60 | ->findByUrlAndUserId($url, $this->getUser()->getId()); | 63 | ->findOneBy([ |
61 | 64 | 'hashedUrl' => $hashedUrl, | |
65 | 'user' => $this->getUser()->getId(), | ||
66 | ]); | ||
62 | $exists = $res instanceof Entry ? $res->getId() : false; | 67 | $exists = $res instanceof Entry ? $res->getId() : false; |
63 | 68 | ||
64 | return $this->sendResponse(['exists' => $exists]); | 69 | return $this->sendResponse(['exists' => $exists]); |
diff --git a/src/Wallabag/CoreBundle/Command/GenerateUrlHashesCommand.php b/src/Wallabag/CoreBundle/Command/GenerateUrlHashesCommand.php new file mode 100644 index 00000000..fe2644f2 --- /dev/null +++ b/src/Wallabag/CoreBundle/Command/GenerateUrlHashesCommand.php | |||
@@ -0,0 +1,95 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Command; | ||
4 | |||
5 | use Doctrine\ORM\NoResultException; | ||
6 | use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; | ||
7 | use Symfony\Component\Console\Input\InputArgument; | ||
8 | use Symfony\Component\Console\Input\InputInterface; | ||
9 | use Symfony\Component\Console\Output\OutputInterface; | ||
10 | use Wallabag\UserBundle\Entity\User; | ||
11 | |||
12 | class GenerateUrlHashesCommand extends ContainerAwareCommand | ||
13 | { | ||
14 | /** @var OutputInterface */ | ||
15 | protected $output; | ||
16 | |||
17 | protected function configure() | ||
18 | { | ||
19 | $this | ||
20 | ->setName('wallabag:generate-hashed-urls') | ||
21 | ->setDescription('Generates hashed urls for each entry') | ||
22 | ->setHelp('This command helps you to generates hashes of the url of each entry, to check through API if an URL is already saved') | ||
23 | ->addArgument( | ||
24 | 'username', | ||
25 | InputArgument::OPTIONAL, | ||
26 | 'User to process entries' | ||
27 | ); | ||
28 | } | ||
29 | |||
30 | protected function execute(InputInterface $input, OutputInterface $output) | ||
31 | { | ||
32 | $this->output = $output; | ||
33 | |||
34 | $username = $input->getArgument('username'); | ||
35 | |||
36 | if ($username) { | ||
37 | try { | ||
38 | $user = $this->getUser($username); | ||
39 | $this->generateHashedUrls($user); | ||
40 | } catch (NoResultException $e) { | ||
41 | $output->writeln(sprintf('<error>User "%s" not found.</error>', $username)); | ||
42 | |||
43 | return 1; | ||
44 | } | ||
45 | } else { | ||
46 | $users = $this->getDoctrine()->getRepository('WallabagUserBundle:User')->findAll(); | ||
47 | |||
48 | $output->writeln(sprintf('Generating hashed urls for the %d user account entries', count($users))); | ||
49 | |||
50 | foreach ($users as $user) { | ||
51 | $output->writeln(sprintf('Processing user %s', $user->getUsername())); | ||
52 | $this->generateHashedUrls($user); | ||
53 | } | ||
54 | $output->writeln(sprintf('Finished generated hashed urls')); | ||
55 | } | ||
56 | |||
57 | return 0; | ||
58 | } | ||
59 | |||
60 | /** | ||
61 | * @param User $user | ||
62 | */ | ||
63 | private function generateHashedUrls(User $user) | ||
64 | { | ||
65 | $em = $this->getContainer()->get('doctrine.orm.entity_manager'); | ||
66 | $repo = $this->getDoctrine()->getRepository('WallabagCoreBundle:Entry'); | ||
67 | |||
68 | $entries = $repo->findByUser($user->getId()); | ||
69 | |||
70 | foreach ($entries as $entry) { | ||
71 | $entry->setHashedUrl(hash('sha512', $entry->getUrl())); | ||
72 | $em->persist($entry); | ||
73 | $em->flush(); | ||
74 | } | ||
75 | |||
76 | $this->output->writeln(sprintf('Generated hashed urls for user %s', $user->getUserName())); | ||
77 | } | ||
78 | |||
79 | /** | ||
80 | * Fetches a user from its username. | ||
81 | * | ||
82 | * @param string $username | ||
83 | * | ||
84 | * @return \Wallabag\UserBundle\Entity\User | ||
85 | */ | ||
86 | private function getUser($username) | ||
87 | { | ||
88 | return $this->getDoctrine()->getRepository('WallabagUserBundle:User')->findOneByUserName($username); | ||
89 | } | ||
90 | |||
91 | private function getDoctrine() | ||
92 | { | ||
93 | return $this->getContainer()->get('doctrine'); | ||
94 | } | ||
95 | } | ||
diff --git a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadEntryData.php b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadEntryData.php index fedad009..22882612 100644 --- a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadEntryData.php +++ b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadEntryData.php | |||
@@ -32,6 +32,7 @@ class LoadEntryData extends AbstractFixture implements OrderedFixtureInterface | |||
32 | 32 | ||
33 | $entry2 = new Entry($this->getReference('admin-user')); | 33 | $entry2 = new Entry($this->getReference('admin-user')); |
34 | $entry2->setUrl('http://0.0.0.0/entry2'); | 34 | $entry2->setUrl('http://0.0.0.0/entry2'); |
35 | $entry2->setHashedUrl(hash('sha512', 'http://0.0.0.0/entry2')); | ||
35 | $entry2->setReadingTime(1); | 36 | $entry2->setReadingTime(1); |
36 | $entry2->setDomainName('domain.io'); | 37 | $entry2->setDomainName('domain.io'); |
37 | $entry2->setMimetype('text/html'); | 38 | $entry2->setMimetype('text/html'); |
diff --git a/src/Wallabag/CoreBundle/Entity/Entry.php b/src/Wallabag/CoreBundle/Entity/Entry.php index 08a67c34..49affe2a 100644 --- a/src/Wallabag/CoreBundle/Entity/Entry.php +++ b/src/Wallabag/CoreBundle/Entity/Entry.php | |||
@@ -24,7 +24,8 @@ use Wallabag\AnnotationBundle\Entity\Annotation; | |||
24 | * options={"collate"="utf8mb4_unicode_ci", "charset"="utf8mb4"}, | 24 | * options={"collate"="utf8mb4_unicode_ci", "charset"="utf8mb4"}, |
25 | * indexes={ | 25 | * indexes={ |
26 | * @ORM\Index(name="created_at", columns={"created_at"}), | 26 | * @ORM\Index(name="created_at", columns={"created_at"}), |
27 | * @ORM\Index(name="uid", columns={"uid"}) | 27 | * @ORM\Index(name="uid", columns={"uid"}), |
28 | * @ORM\Index(name="hashedurl", columns={"hashedurl"}) | ||
28 | * } | 29 | * } |
29 | * ) | 30 | * ) |
30 | * @ORM\HasLifecycleCallbacks() | 31 | * @ORM\HasLifecycleCallbacks() |
@@ -73,6 +74,13 @@ class Entry | |||
73 | private $url; | 74 | private $url; |
74 | 75 | ||
75 | /** | 76 | /** |
77 | * @var string | ||
78 | * | ||
79 | * @ORM\Column(name="hashedurl", type="text", nullable=true) | ||
80 | */ | ||
81 | private $hashedUrl; | ||
82 | |||
83 | /** | ||
76 | * @var bool | 84 | * @var bool |
77 | * | 85 | * |
78 | * @Exclude | 86 | * @Exclude |
@@ -763,4 +771,24 @@ class Entry | |||
763 | 771 | ||
764 | return $this; | 772 | return $this; |
765 | } | 773 | } |
774 | |||
775 | /** | ||
776 | * @return string | ||
777 | */ | ||
778 | public function getHashedUrl() | ||
779 | { | ||
780 | return $this->hashedUrl; | ||
781 | } | ||
782 | |||
783 | /** | ||
784 | * @param mixed $hashedUrl | ||
785 | * | ||
786 | * @return Entry | ||
787 | */ | ||
788 | public function setHashedUrl($hashedUrl) | ||
789 | { | ||
790 | $this->hashedUrl = $hashedUrl; | ||
791 | |||
792 | return $this; | ||
793 | } | ||
766 | } | 794 | } |
diff --git a/src/Wallabag/CoreBundle/Helper/ContentProxy.php b/src/Wallabag/CoreBundle/Helper/ContentProxy.php index 9a08db3d..83ecaa66 100644 --- a/src/Wallabag/CoreBundle/Helper/ContentProxy.php +++ b/src/Wallabag/CoreBundle/Helper/ContentProxy.php | |||
@@ -75,6 +75,7 @@ class ContentProxy | |||
75 | } | 75 | } |
76 | 76 | ||
77 | $entry->setUrl($content['url'] ?: $url); | 77 | $entry->setUrl($content['url'] ?: $url); |
78 | $entry->setHashedUrl(hash('sha512', $entry->getUrl())); | ||
78 | $entry->setTitle($title); | 79 | $entry->setTitle($title); |
79 | $entry->setContent($html); | 80 | $entry->setContent($html); |
80 | $entry->setHttpStatus(isset($content['status']) ? $content['status'] : ''); | 81 | $entry->setHttpStatus(isset($content['status']) ? $content['status'] : ''); |
diff --git a/tests/Wallabag/ApiBundle/Controller/EntryRestControllerTest.php b/tests/Wallabag/ApiBundle/Controller/EntryRestControllerTest.php index bf7d373a..e3a44390 100644 --- a/tests/Wallabag/ApiBundle/Controller/EntryRestControllerTest.php +++ b/tests/Wallabag/ApiBundle/Controller/EntryRestControllerTest.php | |||
@@ -686,7 +686,7 @@ class EntryRestControllerTest extends WallabagApiTestCase | |||
686 | 686 | ||
687 | public function testGetEntriesExists() | 687 | public function testGetEntriesExists() |
688 | { | 688 | { |
689 | $this->client->request('GET', '/api/entries/exists?url=http://0.0.0.0/entry2'); | 689 | $this->client->request('GET', '/api/entries/exists?hashedurl=' . hash('sha512', 'http://0.0.0.0/entry2')); |
690 | 690 | ||
691 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | 691 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); |
692 | 692 | ||
@@ -699,21 +699,21 @@ class EntryRestControllerTest extends WallabagApiTestCase | |||
699 | { | 699 | { |
700 | $url1 = 'http://0.0.0.0/entry2'; | 700 | $url1 = 'http://0.0.0.0/entry2'; |
701 | $url2 = 'http://0.0.0.0/entry10'; | 701 | $url2 = 'http://0.0.0.0/entry10'; |
702 | $this->client->request('GET', '/api/entries/exists?urls[]='.$url1.'&urls[]='.$url2); | 702 | $this->client->request('GET', '/api/entries/exists?hashedurls[]='.hash('sha512',$url1).'&hashedurls[]='.hash('sha512',$url2)); |
703 | 703 | ||
704 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | 704 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); |
705 | 705 | ||
706 | $content = json_decode($this->client->getResponse()->getContent(), true); | 706 | $content = json_decode($this->client->getResponse()->getContent(), true); |
707 | 707 | ||
708 | $this->assertArrayHasKey($url1, $content); | 708 | $this->assertArrayHasKey(hash('sha512', $url1), $content); |
709 | $this->assertArrayHasKey($url2, $content); | 709 | $this->assertArrayHasKey(hash('sha512', $url2), $content); |
710 | $this->assertEquals(2, $content[$url1]); | 710 | $this->assertEquals(2, $content[hash('sha512', $url1)]); |
711 | $this->assertEquals(false, $content[$url2]); | 711 | $this->assertEquals(false, $content[hash('sha512', $url2)]); |
712 | } | 712 | } |
713 | 713 | ||
714 | public function testGetEntriesExistsWhichDoesNotExists() | 714 | public function testGetEntriesExistsWhichDoesNotExists() |
715 | { | 715 | { |
716 | $this->client->request('GET', '/api/entries/exists?url=http://google.com/entry2'); | 716 | $this->client->request('GET', '/api/entries/exists?hashedurl='.hash('sha512','http://google.com/entry2')); |
717 | 717 | ||
718 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | 718 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); |
719 | 719 | ||
@@ -724,7 +724,7 @@ class EntryRestControllerTest extends WallabagApiTestCase | |||
724 | 724 | ||
725 | public function testGetEntriesExistsWithNoUrl() | 725 | public function testGetEntriesExistsWithNoUrl() |
726 | { | 726 | { |
727 | $this->client->request('GET', '/api/entries/exists?url='); | 727 | $this->client->request('GET', '/api/entries/exists?hashedurl='); |
728 | 728 | ||
729 | $this->assertEquals(403, $this->client->getResponse()->getStatusCode()); | 729 | $this->assertEquals(403, $this->client->getResponse()->getStatusCode()); |
730 | } | 730 | } |