aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--app/AppKernel.php3
-rw-r--r--app/config/routing.yml4
-rw-r--r--app/config/routing_rest.yml2
-rw-r--r--src/Wallabag/ApiBundle/Controller/WallabagRestController.php (renamed from src/Wallabag/CoreBundle/Controller/WallabagRestController.php)86
-rw-r--r--src/Wallabag/ApiBundle/DependencyInjection/Configuration.php29
-rw-r--r--src/Wallabag/ApiBundle/DependencyInjection/Security/Factory/WsseFactory.php (renamed from src/Wallabag/CoreBundle/DependencyInjection/Security/Factory/WsseFactory.php)2
-rw-r--r--src/Wallabag/ApiBundle/DependencyInjection/WallabagApiExtension.php25
-rw-r--r--src/Wallabag/ApiBundle/Resources/config/routing.yml0
-rw-r--r--src/Wallabag/ApiBundle/Resources/config/routing_rest.yml4
-rw-r--r--src/Wallabag/ApiBundle/Resources/config/services.yml12
-rw-r--r--src/Wallabag/ApiBundle/Security/Authentication/Provider/WsseProvider.php (renamed from src/Wallabag/CoreBundle/Security/Authentication/Provider/WsseProvider.php)4
-rw-r--r--src/Wallabag/ApiBundle/Security/Authentication/Token/WsseUserToken.php (renamed from src/Wallabag/CoreBundle/Security/Authentication/Token/WsseUserToken.php)2
-rw-r--r--src/Wallabag/ApiBundle/Security/Firewall/WsseListener.php (renamed from src/Wallabag/CoreBundle/Security/Firewall/WsseListener.php)4
-rw-r--r--src/Wallabag/ApiBundle/Tests/Controller/WallabagRestControllerTest.php410
-rw-r--r--src/Wallabag/ApiBundle/WallabagApiBundle.php18
-rw-r--r--src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php6
-rw-r--r--src/Wallabag/CoreBundle/Resources/config/routing_rest.yml4
-rw-r--r--src/Wallabag/CoreBundle/Resources/config/services.yml12
-rw-r--r--src/Wallabag/CoreBundle/Tests/Command/InstallCommandTest.php4
-rw-r--r--src/Wallabag/CoreBundle/Tests/Controller/ConfigControllerTest.php4
-rw-r--r--src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php4
-rw-r--r--src/Wallabag/CoreBundle/Tests/Controller/SecurityControllerTest.php4
-rw-r--r--src/Wallabag/CoreBundle/Tests/Controller/WallabagRestControllerTest.php214
-rw-r--r--src/Wallabag/CoreBundle/Tests/WallabagCoreTestCase.php (renamed from src/Wallabag/CoreBundle/Tests/WallabagTestCase.php)2
-rw-r--r--src/Wallabag/CoreBundle/WallabagCoreBundle.php9
25 files changed, 571 insertions, 297 deletions
diff --git a/app/AppKernel.php b/app/AppKernel.php
index 601b52c3..9a52f349 100644
--- a/app/AppKernel.php
+++ b/app/AppKernel.php
@@ -23,7 +23,8 @@ class AppKernel extends Kernel
23 new Nelmio\CorsBundle\NelmioCorsBundle(), 23 new Nelmio\CorsBundle\NelmioCorsBundle(),
24 new Liip\ThemeBundle\LiipThemeBundle(), 24 new Liip\ThemeBundle\LiipThemeBundle(),
25 new Wallabag\CoreBundle\WallabagCoreBundle(), 25 new Wallabag\CoreBundle\WallabagCoreBundle(),
26 new Bazinga\Bundle\HateoasBundle\BazingaHateoasBundle() 26 new Wallabag\ApiBundle\WallabagApiBundle(),
27 new Bazinga\Bundle\HateoasBundle\BazingaHateoasBundle(),
27 ); 28 );
28 29
29 if (in_array($this->getEnvironment(), array('dev', 'test'))) { 30 if (in_array($this->getEnvironment(), array('dev', 'test'))) {
diff --git a/app/config/routing.yml b/app/config/routing.yml
index d681b39b..8710e97f 100644
--- a/app/config/routing.yml
+++ b/app/config/routing.yml
@@ -1,3 +1,7 @@
1wallabag_api:
2 resource: "@WallabagApiBundle/Resources/config/routing.yml"
3 prefix: /
4
1app: 5app:
2 resource: @WallabagCoreBundle/Controller/ 6 resource: @WallabagCoreBundle/Controller/
3 type: annotation 7 type: annotation
diff --git a/app/config/routing_rest.yml b/app/config/routing_rest.yml
index 82d9e6cc..0a64ad78 100644
--- a/app/config/routing_rest.yml
+++ b/app/config/routing_rest.yml
@@ -1,3 +1,3 @@
1Rest_Wallabag: 1Rest_Wallabag:
2 type : rest 2 type : rest
3 resource: "@WallabagCoreBundle/Resources/config/routing_rest.yml" \ No newline at end of file 3 resource: "@WallabagApiBundle/Resources/config/routing_rest.yml"
diff --git a/src/Wallabag/CoreBundle/Controller/WallabagRestController.php b/src/Wallabag/ApiBundle/Controller/WallabagRestController.php
index 14f42c48..21e4552d 100644
--- a/src/Wallabag/CoreBundle/Controller/WallabagRestController.php
+++ b/src/Wallabag/ApiBundle/Controller/WallabagRestController.php
@@ -1,6 +1,6 @@
1<?php 1<?php
2 2
3namespace Wallabag\CoreBundle\Controller; 3namespace Wallabag\ApiBundle\Controller;
4 4
5use Nelmio\ApiDocBundle\Annotation\ApiDoc; 5use Nelmio\ApiDocBundle\Annotation\ApiDoc;
6use Symfony\Bundle\FrameworkBundle\Controller\Controller; 6use Symfony\Bundle\FrameworkBundle\Controller\Controller;
@@ -62,6 +62,7 @@ class WallabagRestController extends Controller
62 62
63 return array($user->getSalt() ?: null); 63 return array($user->getSalt() ?: null);
64 } 64 }
65
65 /** 66 /**
66 * Retrieve all entries. It could be filtered by many options. 67 * Retrieve all entries. It could be filtered by many options.
67 * 68 *
@@ -86,17 +87,13 @@ class WallabagRestController extends Controller
86 $order = $request->query->get('order', 'desc'); 87 $order = $request->query->get('order', 'desc');
87 $page = (int) $request->query->get('page', 1); 88 $page = (int) $request->query->get('page', 1);
88 $perPage = (int) $request->query->get('perPage', 30); 89 $perPage = (int) $request->query->get('perPage', 30);
89 $tags = $request->query->get('tags', array()); 90 $tags = $request->query->get('tags', []);
90 91
91 $pager = $this 92 $pager = $this
92 ->getDoctrine() 93 ->getDoctrine()
93 ->getRepository('WallabagCoreBundle:Entry') 94 ->getRepository('WallabagCoreBundle:Entry')
94 ->findEntries($this->getUser()->getId(), $isArchived, $isStarred, $sort, $order); 95 ->findEntries($this->getUser()->getId(), $isArchived, $isStarred, $sort, $order);
95 96
96 if (0 === $pager->getNbResults()) {
97 throw $this->createNotFoundException();
98 }
99
100 $pager->setCurrentPage($page); 97 $pager->setCurrentPage($page);
101 $pager->setMaxPerPage($perPage); 98 $pager->setMaxPerPage($perPage);
102 99
@@ -108,7 +105,7 @@ class WallabagRestController extends Controller
108 105
109 $json = $this->get('serializer')->serialize($paginatedCollection, 'json'); 106 $json = $this->get('serializer')->serialize($paginatedCollection, 'json');
110 107
111 return new Response($json, 200, array('application/json')); 108 return $this->renderJsonResponse($json);
112 } 109 }
113 110
114 /** 111 /**
@@ -123,13 +120,11 @@ class WallabagRestController extends Controller
123 */ 120 */
124 public function getEntryAction(Entry $entry) 121 public function getEntryAction(Entry $entry)
125 { 122 {
126 if ($entry->getUser()->getId() != $this->getUser()->getId()) { 123 $this->validateUserAccess($entry->getUser()->getId(), $this->getUser()->getId());
127 throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$entry->getUser()->getId().', logged user id: '.$this->getUser()->getId());
128 }
129 124
130 $json = $this->get('serializer')->serialize($entry, 'json'); 125 $json = $this->get('serializer')->serialize($entry, 'json');
131 126
132 return new Response($json, 200, array('application/json')); 127 return $this->renderJsonResponse($json);
133 } 128 }
134 129
135 /** 130 /**
@@ -165,7 +160,7 @@ class WallabagRestController extends Controller
165 160
166 $json = $this->get('serializer')->serialize($entry, 'json'); 161 $json = $this->get('serializer')->serialize($entry, 'json');
167 162
168 return new Response($json, 200, array('application/json')); 163 return $this->renderJsonResponse($json);
169 } 164 }
170 165
171 /** 166 /**
@@ -186,9 +181,7 @@ class WallabagRestController extends Controller
186 */ 181 */
187 public function patchEntriesAction(Entry $entry, Request $request) 182 public function patchEntriesAction(Entry $entry, Request $request)
188 { 183 {
189 if ($entry->getUser()->getId() != $this->getUser()->getId()) { 184 $this->validateUserAccess($entry->getUser()->getId(), $this->getUser()->getId());
190 throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$entry->getUser()->getId().', logged user id: '.$this->getUser()->getId());
191 }
192 185
193 $title = $request->request->get("title"); 186 $title = $request->request->get("title");
194 $isArchived = $request->request->get("archive"); 187 $isArchived = $request->request->get("archive");
@@ -216,7 +209,7 @@ class WallabagRestController extends Controller
216 209
217 $json = $this->get('serializer')->serialize($entry, 'json'); 210 $json = $this->get('serializer')->serialize($entry, 'json');
218 211
219 return new Response($json, 200, array('application/json')); 212 return $this->renderJsonResponse($json);
220 } 213 }
221 214
222 /** 215 /**
@@ -231,9 +224,7 @@ class WallabagRestController extends Controller
231 */ 224 */
232 public function deleteEntriesAction(Entry $entry) 225 public function deleteEntriesAction(Entry $entry)
233 { 226 {
234 if ($entry->getUser()->getId() != $this->getUser()->getId()) { 227 $this->validateUserAccess($entry->getUser()->getId(), $this->getUser()->getId());
235 throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$entry->getUser()->getId().', logged user id: '.$this->getUser()->getId());
236 }
237 228
238 $em = $this->getDoctrine()->getManager(); 229 $em = $this->getDoctrine()->getManager();
239 $em->remove($entry); 230 $em->remove($entry);
@@ -241,7 +232,7 @@ class WallabagRestController extends Controller
241 232
242 $json = $this->get('serializer')->serialize($entry, 'json'); 233 $json = $this->get('serializer')->serialize($entry, 'json');
243 234
244 return new Response($json, 200, array('application/json')); 235 return $this->renderJsonResponse($json);
245 } 236 }
246 237
247 /** 238 /**
@@ -255,13 +246,11 @@ class WallabagRestController extends Controller
255 */ 246 */
256 public function getEntriesTagsAction(Entry $entry) 247 public function getEntriesTagsAction(Entry $entry)
257 { 248 {
258 if ($entry->getUser()->getId() != $this->getUser()->getId()) { 249 $this->validateUserAccess($entry->getUser()->getId(), $this->getUser()->getId());
259 throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$entry->getUser()->getId().', logged user id: '.$this->getUser()->getId());
260 }
261 250
262 $json = $this->get('serializer')->serialize($entry->getTags(), 'json'); 251 $json = $this->get('serializer')->serialize($entry->getTags(), 'json');
263 252
264 return new Response($json, 200, array('application/json')); 253 return $this->renderJsonResponse($json);
265 } 254 }
266 255
267 /** 256 /**
@@ -278,9 +267,7 @@ class WallabagRestController extends Controller
278 */ 267 */
279 public function postEntriesTagsAction(Request $request, Entry $entry) 268 public function postEntriesTagsAction(Request $request, Entry $entry)
280 { 269 {
281 if ($entry->getUser()->getId() != $this->getUser()->getId()) { 270 $this->validateUserAccess($entry->getUser()->getId(), $this->getUser()->getId());
282 throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$entry->getUser()->getId().', logged user id: '.$this->getUser()->getId());
283 }
284 271
285 $tags = $request->request->get('tags', ''); 272 $tags = $request->request->get('tags', '');
286 if (!empty($tags)) { 273 if (!empty($tags)) {
@@ -293,7 +280,7 @@ class WallabagRestController extends Controller
293 280
294 $json = $this->get('serializer')->serialize($entry, 'json'); 281 $json = $this->get('serializer')->serialize($entry, 'json');
295 282
296 return new Response($json, 200, array('application/json')); 283 return $this->renderJsonResponse($json);
297 } 284 }
298 285
299 /** 286 /**
@@ -301,16 +288,14 @@ class WallabagRestController extends Controller
301 * 288 *
302 * @ApiDoc( 289 * @ApiDoc(
303 * requirements={ 290 * requirements={
304 * {"name"="tag", "dataType"="string", "requirement"="\w+", "description"="The tag"}, 291 * {"name"="tag", "dataType"="integer", "requirement"="\w+", "description"="The tag ID"},
305 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} 292 * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
306 * } 293 * }
307 * ) 294 * )
308 */ 295 */
309 public function deleteEntriesTagsAction(Entry $entry, Tag $tag) 296 public function deleteEntriesTagsAction(Entry $entry, Tag $tag)
310 { 297 {
311 if ($entry->getUser()->getId() != $this->getUser()->getId()) { 298 $this->validateUserAccess($entry->getUser()->getId(), $this->getUser()->getId());
312 throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$entry->getUser()->getId().', logged user id: '.$this->getUser()->getId());
313 }
314 299
315 $entry->removeTag($tag); 300 $entry->removeTag($tag);
316 $em = $this->getDoctrine()->getManager(); 301 $em = $this->getDoctrine()->getManager();
@@ -319,7 +304,7 @@ class WallabagRestController extends Controller
319 304
320 $json = $this->get('serializer')->serialize($entry, 'json'); 305 $json = $this->get('serializer')->serialize($entry, 'json');
321 306
322 return new Response($json, 200, array('application/json')); 307 return $this->renderJsonResponse($json);
323 } 308 }
324 309
325 /** 310 /**
@@ -331,7 +316,7 @@ class WallabagRestController extends Controller
331 { 316 {
332 $json = $this->get('serializer')->serialize($this->getUser()->getTags(), 'json'); 317 $json = $this->get('serializer')->serialize($this->getUser()->getTags(), 'json');
333 318
334 return new Response($json, 200, array('application/json')); 319 return $this->renderJsonResponse($json);
335 } 320 }
336 321
337 /** 322 /**
@@ -339,15 +324,13 @@ class WallabagRestController extends Controller
339 * 324 *
340 * @ApiDoc( 325 * @ApiDoc(
341 * requirements={ 326 * requirements={
342 * {"name"="tag", "dataType"="string", "requirement"="\w+", "description"="The tag"} 327 * {"name"="tag", "dataType"="integer", "requirement"="\w+", "description"="The tag"}
343 * } 328 * }
344 * ) 329 * )
345 */ 330 */
346 public function deleteTagAction(Tag $tag) 331 public function deleteTagAction(Tag $tag)
347 { 332 {
348 if ($tag->getUser()->getId() != $this->getUser()->getId()) { 333 $this->validateUserAccess($tag->getUser()->getId(), $this->getUser()->getId());
349 throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$tag->getUser()->getId().', logged user id: '.$this->getUser()->getId());
350 }
351 334
352 $em = $this->getDoctrine()->getManager(); 335 $em = $this->getDoctrine()->getManager();
353 $em->remove($tag); 336 $em->remove($tag);
@@ -355,6 +338,33 @@ class WallabagRestController extends Controller
355 338
356 $json = $this->get('serializer')->serialize($tag, 'json'); 339 $json = $this->get('serializer')->serialize($tag, 'json');
357 340
341 return $this->renderJsonResponse($json);
342 }
343
344 /**
345 * Validate that the first id is equal to the second one.
346 * If not, throw exception. It means a user try to access information from an other user
347 *
348 * @param integer $requestUserId User id from the requested source
349 * @param integer $currentUserId User id from the retrieved source
350 */
351 private function validateUserAccess($requestUserId, $currentUserId)
352 {
353 if ($requestUserId != $currentUserId) {
354 throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$requestUserId.', logged user id: '.$currentUserId);
355 }
356 }
357
358 /**
359 * Send a JSON Response.
360 * We don't use the Symfony JsonRespone, because it takes an array as parameter instead of a JSON string
361 *
362 * @param string $json
363 *
364 * @return Response
365 */
366 private function renderJsonResponse($json)
367 {
358 return new Response($json, 200, array('application/json')); 368 return new Response($json, 200, array('application/json'));
359 } 369 }
360} 370}
diff --git a/src/Wallabag/ApiBundle/DependencyInjection/Configuration.php b/src/Wallabag/ApiBundle/DependencyInjection/Configuration.php
new file mode 100644
index 00000000..80a07ca2
--- /dev/null
+++ b/src/Wallabag/ApiBundle/DependencyInjection/Configuration.php
@@ -0,0 +1,29 @@
1<?php
2
3namespace Wallabag\ApiBundle\DependencyInjection;
4
5use Symfony\Component\Config\Definition\Builder\TreeBuilder;
6use Symfony\Component\Config\Definition\ConfigurationInterface;
7
8/**
9 * This is the class that validates and merges configuration from your app/config files
10 *
11 * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class}
12 */
13class Configuration implements ConfigurationInterface
14{
15 /**
16 * {@inheritdoc}
17 */
18 public function getConfigTreeBuilder()
19 {
20 $treeBuilder = new TreeBuilder();
21 $rootNode = $treeBuilder->root('wallabag_api');
22
23 // Here you should define the parameters that are allowed to
24 // configure your bundle. See the documentation linked above for
25 // more information on that topic.
26
27 return $treeBuilder;
28 }
29}
diff --git a/src/Wallabag/CoreBundle/DependencyInjection/Security/Factory/WsseFactory.php b/src/Wallabag/ApiBundle/DependencyInjection/Security/Factory/WsseFactory.php
index 0b5bdb40..402eb869 100644
--- a/src/Wallabag/CoreBundle/DependencyInjection/Security/Factory/WsseFactory.php
+++ b/src/Wallabag/ApiBundle/DependencyInjection/Security/Factory/WsseFactory.php
@@ -1,6 +1,6 @@
1<?php 1<?php
2 2
3namespace Wallabag\CoreBundle\DependencyInjection\Security\Factory; 3namespace Wallabag\ApiBundle\DependencyInjection\Security\Factory;
4 4
5use Symfony\Component\DependencyInjection\ContainerBuilder; 5use Symfony\Component\DependencyInjection\ContainerBuilder;
6use Symfony\Component\DependencyInjection\Reference; 6use Symfony\Component\DependencyInjection\Reference;
diff --git a/src/Wallabag/ApiBundle/DependencyInjection/WallabagApiExtension.php b/src/Wallabag/ApiBundle/DependencyInjection/WallabagApiExtension.php
new file mode 100644
index 00000000..c5cc204e
--- /dev/null
+++ b/src/Wallabag/ApiBundle/DependencyInjection/WallabagApiExtension.php
@@ -0,0 +1,25 @@
1<?php
2
3namespace Wallabag\ApiBundle\DependencyInjection;
4
5use Symfony\Component\DependencyInjection\ContainerBuilder;
6use Symfony\Component\Config\FileLocator;
7use Symfony\Component\HttpKernel\DependencyInjection\Extension;
8use Symfony\Component\DependencyInjection\Loader;
9
10class WallabagApiExtension extends Extension
11{
12 public function load(array $configs, ContainerBuilder $container)
13 {
14 $configuration = new Configuration();
15 $config = $this->processConfiguration($configuration, $configs);
16
17 $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
18 $loader->load('services.yml');
19 }
20
21 public function getAlias()
22 {
23 return 'wallabag_api';
24 }
25}
diff --git a/src/Wallabag/ApiBundle/Resources/config/routing.yml b/src/Wallabag/ApiBundle/Resources/config/routing.yml
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/Wallabag/ApiBundle/Resources/config/routing.yml
diff --git a/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml b/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml
new file mode 100644
index 00000000..5f43f971
--- /dev/null
+++ b/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml
@@ -0,0 +1,4 @@
1entries:
2 type: rest
3 resource: "WallabagApiBundle:WallabagRest"
4 name_prefix: api_
diff --git a/src/Wallabag/ApiBundle/Resources/config/services.yml b/src/Wallabag/ApiBundle/Resources/config/services.yml
new file mode 100644
index 00000000..6854a444
--- /dev/null
+++ b/src/Wallabag/ApiBundle/Resources/config/services.yml
@@ -0,0 +1,12 @@
1services:
2 wsse.security.authentication.provider:
3 class: Wallabag\ApiBundle\Security\Authentication\Provider\WsseProvider
4 public: false
5 arguments: ['', '%kernel.cache_dir%/security/nonces']
6
7 wsse.security.authentication.listener:
8 class: Wallabag\ApiBundle\Security\Firewall\WsseListener
9 public: false
10 tags:
11 - { name: monolog.logger, channel: wsse }
12 arguments: ['@security.context', '@security.authentication.manager', '@logger']
diff --git a/src/Wallabag/CoreBundle/Security/Authentication/Provider/WsseProvider.php b/src/Wallabag/ApiBundle/Security/Authentication/Provider/WsseProvider.php
index 7e6a5dfb..8e49167a 100644
--- a/src/Wallabag/CoreBundle/Security/Authentication/Provider/WsseProvider.php
+++ b/src/Wallabag/ApiBundle/Security/Authentication/Provider/WsseProvider.php
@@ -1,12 +1,12 @@
1<?php 1<?php
2namespace Wallabag\CoreBundle\Security\Authentication\Provider; 2namespace Wallabag\ApiBundle\Security\Authentication\Provider;
3 3
4use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface; 4use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
5use Symfony\Component\Security\Core\User\UserProviderInterface; 5use Symfony\Component\Security\Core\User\UserProviderInterface;
6use Symfony\Component\Security\Core\Exception\AuthenticationException; 6use Symfony\Component\Security\Core\Exception\AuthenticationException;
7use Symfony\Component\Security\Core\Exception\NonceExpiredException; 7use Symfony\Component\Security\Core\Exception\NonceExpiredException;
8use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; 8use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
9use Wallabag\CoreBundle\Security\Authentication\Token\WsseUserToken; 9use Wallabag\ApiBundle\Security\Authentication\Token\WsseUserToken;
10 10
11class WsseProvider implements AuthenticationProviderInterface 11class WsseProvider implements AuthenticationProviderInterface
12{ 12{
diff --git a/src/Wallabag/CoreBundle/Security/Authentication/Token/WsseUserToken.php b/src/Wallabag/ApiBundle/Security/Authentication/Token/WsseUserToken.php
index ea6fb9bf..aa68dbdc 100644
--- a/src/Wallabag/CoreBundle/Security/Authentication/Token/WsseUserToken.php
+++ b/src/Wallabag/ApiBundle/Security/Authentication/Token/WsseUserToken.php
@@ -1,5 +1,5 @@
1<?php 1<?php
2namespace Wallabag\CoreBundle\Security\Authentication\Token; 2namespace Wallabag\ApiBundle\Security\Authentication\Token;
3 3
4use Symfony\Component\Security\Core\Authentication\Token\AbstractToken; 4use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;
5 5
diff --git a/src/Wallabag/CoreBundle/Security/Firewall/WsseListener.php b/src/Wallabag/ApiBundle/Security/Firewall/WsseListener.php
index 6ffdfaf0..50587837 100644
--- a/src/Wallabag/CoreBundle/Security/Firewall/WsseListener.php
+++ b/src/Wallabag/ApiBundle/Security/Firewall/WsseListener.php
@@ -1,6 +1,6 @@
1<?php 1<?php
2 2
3namespace Wallabag\CoreBundle\Security\Firewall; 3namespace Wallabag\ApiBundle\Security\Firewall;
4 4
5use Symfony\Component\HttpFoundation\Response; 5use Symfony\Component\HttpFoundation\Response;
6use Symfony\Component\HttpKernel\Event\GetResponseEvent; 6use Symfony\Component\HttpKernel\Event\GetResponseEvent;
@@ -8,7 +8,7 @@ use Symfony\Component\Security\Http\Firewall\ListenerInterface;
8use Symfony\Component\Security\Core\Exception\AuthenticationException; 8use Symfony\Component\Security\Core\Exception\AuthenticationException;
9use Symfony\Component\Security\Core\SecurityContextInterface; 9use Symfony\Component\Security\Core\SecurityContextInterface;
10use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; 10use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
11use Wallabag\CoreBundle\Security\Authentication\Token\WsseUserToken; 11use Wallabag\ApiBundle\Security\Authentication\Token\WsseUserToken;
12use Psr\Log\LoggerInterface; 12use Psr\Log\LoggerInterface;
13 13
14class WsseListener implements ListenerInterface 14class WsseListener implements ListenerInterface
diff --git a/src/Wallabag/ApiBundle/Tests/Controller/WallabagRestControllerTest.php b/src/Wallabag/ApiBundle/Tests/Controller/WallabagRestControllerTest.php
new file mode 100644
index 00000000..ea8ee072
--- /dev/null
+++ b/src/Wallabag/ApiBundle/Tests/Controller/WallabagRestControllerTest.php
@@ -0,0 +1,410 @@
1<?php
2
3namespace Wallabag\CoreBundle\Tests\Controller;
4
5use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
6
7class WallabagRestControllerTest extends WebTestCase
8{
9 protected static $salt;
10
11 /**
12 * Grab the salt once and store it to be available for all tests
13 */
14 public static function setUpBeforeClass()
15 {
16 $client = self::createClient();
17
18 $user = $client->getContainer()
19 ->get('doctrine.orm.entity_manager')
20 ->getRepository('WallabagCoreBundle:User')
21 ->findOneByUsername('admin');
22
23 self::$salt = $user->getSalt();
24 }
25
26 /**
27 * Generate HTTP headers for authenticate user on API
28 *
29 * @param string $username
30 * @param string $password
31 *
32 * @return array
33 */
34 private function generateHeaders($username, $password)
35 {
36 $encryptedPassword = sha1($password.$username.self::$salt);
37 $nonce = substr(md5(uniqid('nonce_', true)), 0, 16);
38
39 $now = new \DateTime('now', new \DateTimeZone('UTC'));
40 $created = (string) $now->format('Y-m-d\TH:i:s\Z');
41 $digest = base64_encode(sha1(base64_decode($nonce).$created.$encryptedPassword, true));
42
43 return array(
44 'HTTP_AUTHORIZATION' => 'Authorization profile="UsernameToken"',
45 'HTTP_x-wsse' => 'X-WSSE: UsernameToken Username="'.$username.'", PasswordDigest="'.$digest.'", Nonce="'.$nonce.'", Created="'.$created.'"',
46 );
47 }
48
49 public function testGetSalt()
50 {
51 $client = $this->createClient();
52 $client->request('GET', '/api/salts/admin.json');
53
54 $user = $client->getContainer()
55 ->get('doctrine.orm.entity_manager')
56 ->getRepository('WallabagCoreBundle:User')
57 ->findOneByUsername('admin');
58
59 $this->assertEquals(200, $client->getResponse()->getStatusCode());
60
61 $content = json_decode($client->getResponse()->getContent(), true);
62
63 $this->assertArrayHasKey(0, $content);
64 $this->assertEquals($user->getSalt(), $content[0]);
65
66 $client->request('GET', '/api/salts/notfound.json');
67 $this->assertEquals(404, $client->getResponse()->getStatusCode());
68 }
69
70 public function testWithBadHeaders()
71 {
72 $client = $this->createClient();
73
74 $entry = $client->getContainer()
75 ->get('doctrine.orm.entity_manager')
76 ->getRepository('WallabagCoreBundle:Entry')
77 ->findOneByIsArchived(false);
78
79 if (!$entry) {
80 $this->markTestSkipped('No content found in db.');
81 }
82
83 $badHeaders = array(
84 'HTTP_AUTHORIZATION' => 'Authorization profile="UsernameToken"',
85 'HTTP_x-wsse' => 'X-WSSE: UsernameToken Username="admin", PasswordDigest="Wr0ngDig3st", Nonce="n0Nc3", Created="2015-01-01T13:37:00Z"',
86 );
87
88 $client->request('GET', '/api/entries/'.$entry->getId().'.json', array(), array(), $badHeaders);
89 $this->assertEquals(403, $client->getResponse()->getStatusCode());
90 }
91
92 public function testGetOneEntry()
93 {
94 $client = $this->createClient();
95 $headers = $this->generateHeaders('admin', 'mypassword');
96
97 $entry = $client->getContainer()
98 ->get('doctrine.orm.entity_manager')
99 ->getRepository('WallabagCoreBundle:Entry')
100 ->findOneBy(array('user' => 1, 'isArchived' => false));
101
102 if (!$entry) {
103 $this->markTestSkipped('No content found in db.');
104 }
105
106 $client->request('GET', '/api/entries/'.$entry->getId().'.json', array(), array(), $headers);
107
108 $this->assertEquals(200, $client->getResponse()->getStatusCode());
109
110 $content = json_decode($client->getResponse()->getContent(), true);
111
112 $this->assertEquals($entry->getTitle(), $content['title']);
113 $this->assertEquals($entry->getUrl(), $content['url']);
114 $this->assertCount(count($entry->getTags()), $content['tags']);
115
116 $this->assertTrue(
117 $client->getResponse()->headers->contains(
118 'Content-Type',
119 'application/json'
120 )
121 );
122 }
123
124 public function testGetOneEntryWrongUser()
125 {
126 $client = $this->createClient();
127 $headers = $this->generateHeaders('admin', 'mypassword');
128
129 $entry = $client->getContainer()
130 ->get('doctrine.orm.entity_manager')
131 ->getRepository('WallabagCoreBundle:Entry')
132 ->findOneBy(array('user' => 2, 'isArchived' => false));
133
134 if (!$entry) {
135 $this->markTestSkipped('No content found in db.');
136 }
137
138 $client->request('GET', '/api/entries/'.$entry->getId().'.json', array(), array(), $headers);
139
140 $this->assertEquals(403, $client->getResponse()->getStatusCode());
141 }
142
143 public function testGetEntries()
144 {
145 $client = $this->createClient();
146 $headers = $this->generateHeaders('admin', 'mypassword');
147
148 $client->request('GET', '/api/entries', array(), array(), $headers);
149
150 $this->assertEquals(200, $client->getResponse()->getStatusCode());
151
152 $content = json_decode($client->getResponse()->getContent(), true);
153
154 $this->assertGreaterThanOrEqual(1, count($content));
155 $this->assertNotEmpty($content['_embedded']['items']);
156 $this->assertGreaterThanOrEqual(1, $content['total']);
157 $this->assertEquals(1, $content['page']);
158 $this->assertGreaterThanOrEqual(1, $content['pages']);
159
160 $this->assertTrue(
161 $client->getResponse()->headers->contains(
162 'Content-Type',
163 'application/json'
164 )
165 );
166 }
167
168 public function testGetStarredEntries()
169 {
170 $client = $this->createClient();
171 $headers = $this->generateHeaders('admin', 'mypassword');
172
173 $client->request('GET', '/api/entries', array('archive' => 1), array(), $headers);
174
175 $this->assertEquals(200, $client->getResponse()->getStatusCode());
176
177 $content = json_decode($client->getResponse()->getContent(), true);
178
179 $this->assertGreaterThanOrEqual(1, count($content));
180 $this->assertEmpty($content['_embedded']['items']);
181 $this->assertEquals(0, $content['total']);
182 $this->assertEquals(1, $content['page']);
183 $this->assertEquals(1, $content['pages']);
184
185 $this->assertTrue(
186 $client->getResponse()->headers->contains(
187 'Content-Type',
188 'application/json'
189 )
190 );
191 }
192
193 public function testDeleteEntry()
194 {
195 $client = $this->createClient();
196 $headers = $this->generateHeaders('admin', 'mypassword');
197
198 $entry = $client->getContainer()
199 ->get('doctrine.orm.entity_manager')
200 ->getRepository('WallabagCoreBundle:Entry')
201 ->findOneByUser(1);
202
203 if (!$entry) {
204 $this->markTestSkipped('No content found in db.');
205 }
206
207 $client->request('DELETE', '/api/entries/'.$entry->getId().'.json', array(), array(), $headers);
208
209 $this->assertEquals(200, $client->getResponse()->getStatusCode());
210
211 $content = json_decode($client->getResponse()->getContent(), true);
212
213 $this->assertEquals($entry->getTitle(), $content['title']);
214 $this->assertEquals($entry->getUrl(), $content['url']);
215
216 // We'll try to delete this entry again
217 $headers = $this->generateHeaders('admin', 'mypassword');
218
219 $client->request('DELETE', '/api/entries/'.$entry->getId().'.json', array(), array(), $headers);
220
221 $this->assertEquals(404, $client->getResponse()->getStatusCode());
222 }
223
224 public function testPostEntry()
225 {
226 $client = $this->createClient();
227 $headers = $this->generateHeaders('admin', 'mypassword');
228
229 $client->request('POST', '/api/entries.json', array(
230 'url' => 'http://www.lemonde.fr/pixels/article/2015/03/28/plongee-dans-l-univers-d-ingress-le-jeu-de-google-aux-frontieres-du-reel_4601155_4408996.html',
231 'tags' => 'google',
232 ), array(), $headers);
233
234 $this->assertEquals(200, $client->getResponse()->getStatusCode());
235
236 $content = json_decode($client->getResponse()->getContent(), true);
237
238 $this->assertGreaterThan(0, $content['id']);
239 $this->assertEquals('http://www.lemonde.fr/pixels/article/2015/03/28/plongee-dans-l-univers-d-ingress-le-jeu-de-google-aux-frontieres-du-reel_4601155_4408996.html', $content['url']);
240 $this->assertEquals(false, $content['is_archived']);
241 $this->assertEquals(false, $content['is_starred']);
242 $this->assertCount(1, $content['tags']);
243 }
244
245 public function testPatchEntry()
246 {
247 $client = $this->createClient();
248 $headers = $this->generateHeaders('admin', 'mypassword');
249
250 $entry = $client->getContainer()
251 ->get('doctrine.orm.entity_manager')
252 ->getRepository('WallabagCoreBundle:Entry')
253 ->findOneByUser(1);
254
255 if (!$entry) {
256 $this->markTestSkipped('No content found in db.');
257 }
258
259 // hydrate the tags relations
260 $nbTags = count($entry->getTags());
261
262 $client->request('PATCH', '/api/entries/'.$entry->getId().'.json', array(
263 'title' => 'New awesome title',
264 'tags' => 'new tag '.uniqid(),
265 'star' => true,
266 'archive' => false,
267 ), array(), $headers);
268
269 $this->assertEquals(200, $client->getResponse()->getStatusCode());
270
271 $content = json_decode($client->getResponse()->getContent(), true);
272
273 $this->assertEquals($entry->getId(), $content['id']);
274 $this->assertEquals($entry->getUrl(), $content['url']);
275 $this->assertEquals('New awesome title', $content['title']);
276 $this->assertGreaterThan($nbTags, count($content['tags']));
277 }
278
279 public function testGetTagsEntry()
280 {
281 $client = $this->createClient();
282 $headers = $this->generateHeaders('admin', 'mypassword');
283
284 $entry = $client->getContainer()
285 ->get('doctrine.orm.entity_manager')
286 ->getRepository('WallabagCoreBundle:Entry')
287 ->findOneWithTags(1);
288
289 $entry = $entry[0];
290
291 if (!$entry) {
292 $this->markTestSkipped('No content found in db.');
293 }
294
295 $tags = array();
296 foreach ($entry->getTags() as $tag) {
297 $tags[] = array('id' => $tag->getId(), 'label' => $tag->getLabel());
298 }
299
300 $client->request('GET', '/api/entries/'.$entry->getId().'/tags', array(), array(), $headers);
301
302 $this->assertEquals(json_encode($tags, JSON_HEX_QUOT), $client->getResponse()->getContent());
303 }
304
305 public function testPostTagsOnEntry()
306 {
307 $client = $this->createClient();
308 $headers = $this->generateHeaders('admin', 'mypassword');
309
310 $entry = $client->getContainer()
311 ->get('doctrine.orm.entity_manager')
312 ->getRepository('WallabagCoreBundle:Entry')
313 ->findOneByUser(1);
314
315 if (!$entry) {
316 $this->markTestSkipped('No content found in db.');
317 }
318
319 $nbTags = count($entry->getTags());
320
321 $newTags = 'tag1,tag2,tag3';
322
323 $client->request('POST', '/api/entries/'.$entry->getId().'/tags', array('tags' => $newTags), array(), $headers);
324
325 $this->assertEquals(200, $client->getResponse()->getStatusCode());
326
327 $content = json_decode($client->getResponse()->getContent(), true);
328
329 $this->assertArrayHasKey('tags', $content);
330 $this->assertEquals($nbTags+3, count($content['tags']));
331
332 $entryDB = $client->getContainer()
333 ->get('doctrine.orm.entity_manager')
334 ->getRepository('WallabagCoreBundle:Entry')
335 ->find($entry->getId());
336
337 $tagsInDB = array();
338 foreach ($entryDB->getTags()->toArray() as $tag) {
339 $tagsInDB[$tag->getId()] = $tag->getLabel();
340 }
341
342 foreach (explode(',', $newTags) as $tag) {
343 $this->assertContains($tag, $tagsInDB);
344 }
345 }
346
347 public function testDeleteOneTagEntrie()
348 {
349 $client = $this->createClient();
350 $headers = $this->generateHeaders('admin', 'mypassword');
351
352 $entry = $client->getContainer()
353 ->get('doctrine.orm.entity_manager')
354 ->getRepository('WallabagCoreBundle:Entry')
355 ->findOneByUser(1);
356
357 if (!$entry) {
358 $this->markTestSkipped('No content found in db.');
359 }
360
361 // hydrate the tags relations
362 $nbTags = count($entry->getTags());
363 $tag = $entry->getTags()[0];
364
365 $client->request('DELETE', '/api/entries/'.$entry->getId().'/tags/'.$tag->getId().'.json', array(), array(), $headers);
366
367 $this->assertEquals(200, $client->getResponse()->getStatusCode());
368
369 $content = json_decode($client->getResponse()->getContent(), true);
370
371 $this->assertArrayHasKey('tags', $content);
372 $this->assertEquals($nbTags-1, count($content['tags']));
373 }
374
375 public function testGetUserTags()
376 {
377 $client = $this->createClient();
378 $headers = $this->generateHeaders('admin', 'mypassword');
379
380 $client->request('GET', '/api/tags.json', array(), array(), $headers);
381
382 $this->assertEquals(200, $client->getResponse()->getStatusCode());
383
384 $content = json_decode($client->getResponse()->getContent(), true);
385
386 $this->assertGreaterThan(0, $content);
387 $this->assertArrayHasKey('id', $content[0]);
388 $this->assertArrayHasKey('label', $content[0]);
389
390 return end($content);
391 }
392
393 /**
394 * @depends testGetUserTags
395 */
396 public function testDeleteUserTag($tag)
397 {
398 $client = $this->createClient();
399 $headers = $this->generateHeaders('admin', 'mypassword');
400
401 $client->request('DELETE', '/api/tags/'.$tag['id'].'.json', array(), array(), $headers);
402
403 $this->assertEquals(200, $client->getResponse()->getStatusCode());
404
405 $content = json_decode($client->getResponse()->getContent(), true);
406
407 $this->assertArrayHasKey('label', $content);
408 $this->assertEquals($tag['label'], $content['label']);
409 }
410}
diff --git a/src/Wallabag/ApiBundle/WallabagApiBundle.php b/src/Wallabag/ApiBundle/WallabagApiBundle.php
new file mode 100644
index 00000000..2484f277
--- /dev/null
+++ b/src/Wallabag/ApiBundle/WallabagApiBundle.php
@@ -0,0 +1,18 @@
1<?php
2
3namespace Wallabag\ApiBundle;
4
5use Symfony\Component\HttpKernel\Bundle\Bundle;
6use Wallabag\ApiBundle\DependencyInjection\Security\Factory\WsseFactory;
7use Symfony\Component\DependencyInjection\ContainerBuilder;
8
9class WallabagApiBundle extends Bundle
10{
11 public function build(ContainerBuilder $container)
12 {
13 parent::build($container);
14
15 $extension = $container->getExtension('security');
16 $extension->addSecurityListenerFactory(new WsseFactory());
17 }
18}
diff --git a/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php b/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php
index c6ecc99e..7493351b 100644
--- a/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php
+++ b/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php
@@ -3,15 +3,15 @@
3namespace Wallabag\CoreBundle\DependencyInjection; 3namespace Wallabag\CoreBundle\DependencyInjection;
4 4
5use Symfony\Component\DependencyInjection\ContainerBuilder; 5use Symfony\Component\DependencyInjection\ContainerBuilder;
6use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
7use Symfony\Component\HttpKernel\DependencyInjection\Extension;
8use Symfony\Component\Config\FileLocator; 6use Symfony\Component\Config\FileLocator;
7use Symfony\Component\HttpKernel\DependencyInjection\Extension;
8use Symfony\Component\DependencyInjection\Loader;
9 9
10class WallabagCoreExtension extends Extension 10class WallabagCoreExtension extends Extension
11{ 11{
12 public function load(array $configs, ContainerBuilder $container) 12 public function load(array $configs, ContainerBuilder $container)
13 { 13 {
14 $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); 14 $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
15 $loader->load('services.yml'); 15 $loader->load('services.yml');
16 } 16 }
17 17
diff --git a/src/Wallabag/CoreBundle/Resources/config/routing_rest.yml b/src/Wallabag/CoreBundle/Resources/config/routing_rest.yml
deleted file mode 100644
index d3af6b72..00000000
--- a/src/Wallabag/CoreBundle/Resources/config/routing_rest.yml
+++ /dev/null
@@ -1,4 +0,0 @@
1entries:
2 type: rest
3 resource: "WallabagCoreBundle:WallabagRest"
4 name_prefix: api_
diff --git a/src/Wallabag/CoreBundle/Resources/config/services.yml b/src/Wallabag/CoreBundle/Resources/config/services.yml
index cea6c0df..d8bd8d52 100644
--- a/src/Wallabag/CoreBundle/Resources/config/services.yml
+++ b/src/Wallabag/CoreBundle/Resources/config/services.yml
@@ -4,18 +4,6 @@ services:
4 tags: 4 tags:
5 - { name: twig.extension } 5 - { name: twig.extension }
6 6
7 wsse.security.authentication.provider:
8 class: Wallabag\CoreBundle\Security\Authentication\Provider\WsseProvider
9 public: false
10 arguments: ['', '%kernel.cache_dir%/security/nonces']
11
12 wsse.security.authentication.listener:
13 class: Wallabag\CoreBundle\Security\Firewall\WsseListener
14 public: false
15 tags:
16 - { name: monolog.logger, channel: wsse }
17 arguments: ['@security.context', '@security.authentication.manager', '@logger']
18
19 wallabag_core.helper.detect_active_theme: 7 wallabag_core.helper.detect_active_theme:
20 class: Wallabag\CoreBundle\Helper\DetectActiveTheme 8 class: Wallabag\CoreBundle\Helper\DetectActiveTheme
21 arguments: 9 arguments:
diff --git a/src/Wallabag/CoreBundle/Tests/Command/InstallCommandTest.php b/src/Wallabag/CoreBundle/Tests/Command/InstallCommandTest.php
index f689b532..7a819953 100644
--- a/src/Wallabag/CoreBundle/Tests/Command/InstallCommandTest.php
+++ b/src/Wallabag/CoreBundle/Tests/Command/InstallCommandTest.php
@@ -2,7 +2,7 @@
2 2
3namespace Wallabag\CoreBundle\Tests\Command; 3namespace Wallabag\CoreBundle\Tests\Command;
4 4
5use Wallabag\CoreBundle\Tests\WallabagTestCase; 5use Wallabag\CoreBundle\Tests\WallabagCoreTestCase;
6use Wallabag\CoreBundle\Command\InstallCommand; 6use Wallabag\CoreBundle\Command\InstallCommand;
7use Wallabag\CoreBundle\Tests\Mock\InstallCommandMock; 7use Wallabag\CoreBundle\Tests\Mock\InstallCommandMock;
8use Symfony\Bundle\FrameworkBundle\Console\Application; 8use Symfony\Bundle\FrameworkBundle\Console\Application;
@@ -12,7 +12,7 @@ use Symfony\Component\Console\Output\NullOutput;
12use Doctrine\Bundle\DoctrineBundle\Command\DropDatabaseDoctrineCommand; 12use Doctrine\Bundle\DoctrineBundle\Command\DropDatabaseDoctrineCommand;
13use Doctrine\Bundle\DoctrineBundle\Command\CreateDatabaseDoctrineCommand; 13use Doctrine\Bundle\DoctrineBundle\Command\CreateDatabaseDoctrineCommand;
14 14
15class InstallCommandTest extends WallabagTestCase 15class InstallCommandTest extends WallabagCoreTestCase
16{ 16{
17 public static function tearDownAfterClass() 17 public static function tearDownAfterClass()
18 { 18 {
diff --git a/src/Wallabag/CoreBundle/Tests/Controller/ConfigControllerTest.php b/src/Wallabag/CoreBundle/Tests/Controller/ConfigControllerTest.php
index 5030bcbd..3c158922 100644
--- a/src/Wallabag/CoreBundle/Tests/Controller/ConfigControllerTest.php
+++ b/src/Wallabag/CoreBundle/Tests/Controller/ConfigControllerTest.php
@@ -2,9 +2,9 @@
2 2
3namespace Wallabag\CoreBundle\Tests\Controller; 3namespace Wallabag\CoreBundle\Tests\Controller;
4 4
5use Wallabag\CoreBundle\Tests\WallabagTestCase; 5use Wallabag\CoreBundle\Tests\WallabagCoreTestCase;
6 6
7class ConfigControllerTest extends WallabagTestCase 7class ConfigControllerTest extends WallabagCoreTestCase
8{ 8{
9 public function testLogin() 9 public function testLogin()
10 { 10 {
diff --git a/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php b/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php
index 1a0d586c..8a7fdda2 100644
--- a/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php
+++ b/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php
@@ -2,10 +2,10 @@
2 2
3namespace Wallabag\CoreBundle\Tests\Controller; 3namespace Wallabag\CoreBundle\Tests\Controller;
4 4
5use Wallabag\CoreBundle\Tests\WallabagTestCase; 5use Wallabag\CoreBundle\Tests\WallabagCoreTestCase;
6use Doctrine\ORM\AbstractQuery; 6use Doctrine\ORM\AbstractQuery;
7 7
8class EntryControllerTest extends WallabagTestCase 8class EntryControllerTest extends WallabagCoreTestCase
9{ 9{
10 public function testLogin() 10 public function testLogin()
11 { 11 {
diff --git a/src/Wallabag/CoreBundle/Tests/Controller/SecurityControllerTest.php b/src/Wallabag/CoreBundle/Tests/Controller/SecurityControllerTest.php
index 1dd05f89..e560ffdd 100644
--- a/src/Wallabag/CoreBundle/Tests/Controller/SecurityControllerTest.php
+++ b/src/Wallabag/CoreBundle/Tests/Controller/SecurityControllerTest.php
@@ -2,11 +2,11 @@
2 2
3namespace Wallabag\CoreBundle\Tests\Controller; 3namespace Wallabag\CoreBundle\Tests\Controller;
4 4
5use Wallabag\CoreBundle\Tests\WallabagTestCase;
6use Symfony\Component\Filesystem\Filesystem; 5use Symfony\Component\Filesystem\Filesystem;
7use Symfony\Component\Finder\Finder; 6use Symfony\Component\Finder\Finder;
7use Wallabag\CoreBundle\Tests\WallabagCoreTestCase;
8 8
9class SecurityControllerTest extends WallabagTestCase 9class SecurityControllerTest extends WallabagCoreTestCase
10{ 10{
11 public function testLogin() 11 public function testLogin()
12 { 12 {
diff --git a/src/Wallabag/CoreBundle/Tests/Controller/WallabagRestControllerTest.php b/src/Wallabag/CoreBundle/Tests/Controller/WallabagRestControllerTest.php
deleted file mode 100644
index c9907065..00000000
--- a/src/Wallabag/CoreBundle/Tests/Controller/WallabagRestControllerTest.php
+++ /dev/null
@@ -1,214 +0,0 @@
1<?php
2
3namespace Wallabag\CoreBundle\Tests\Controller;
4
5use Wallabag\CoreBundle\Tests\WallabagTestCase;
6
7class WallabagRestControllerTest extends WallabagTestCase
8{
9 /**
10 * Generate HTTP headers for authenticate user on API
11 *
12 * @param $username
13 * @param $password
14 * @param $salt
15 *
16 * @return array
17 */
18 private function generateHeaders($username, $password, $salt)
19 {
20 $encryptedPassword = sha1($password.$username.$salt);
21 $nonce = substr(md5(uniqid('nonce_', true)), 0, 16);
22
23 $now = new \DateTime('now', new \DateTimeZone('UTC'));
24 $created = (string) $now->format('Y-m-d\TH:i:s\Z');
25 $digest = base64_encode(sha1(base64_decode($nonce).$created.$encryptedPassword, true));
26
27 return array(
28 'HTTP_AUTHORIZATION' => 'Authorization profile="UsernameToken"',
29 'HTTP_x-wsse' => 'X-WSSE: UsernameToken Username="'.$username.'", PasswordDigest="'.$digest.'", Nonce="'.$nonce.'", Created="'.$created.'"',
30 );
31 }
32
33 public function testGetSalt()
34 {
35 $client = $this->createClient();
36 $client->request('GET', '/api/salts/admin.json');
37 $this->assertEquals(200, $client->getResponse()->getStatusCode());
38 $this->assertNotEmpty(json_decode($client->getResponse()->getContent()));
39
40 $client->request('GET', '/api/salts/notfound.json');
41 $this->assertEquals(404, $client->getResponse()->getStatusCode());
42 }
43
44 public function testWithBadHeaders()
45 {
46 $client = $this->createClient();
47
48 $entry = $client->getContainer()
49 ->get('doctrine.orm.entity_manager')
50 ->getRepository('WallabagCoreBundle:Entry')
51 ->findOneByIsArchived(false);
52
53 if (!$entry) {
54 $this->markTestSkipped('No content found in db.');
55 }
56
57 $badHeaders = array(
58 'HTTP_AUTHORIZATION' => 'Authorization profile="UsernameToken"',
59 'HTTP_x-wsse' => 'X-WSSE: UsernameToken Username="admin", PasswordDigest="Wr0ngDig3st", Nonce="n0Nc3", Created="2015-01-01T13:37:00Z"',
60 );
61
62 $client->request('GET', '/api/entries/'.$entry->getId().'.json', array(), array(), $badHeaders);
63 $this->assertEquals(403, $client->getResponse()->getStatusCode());
64 }
65
66 public function testGetOneEntry()
67 {
68 $client = $this->createClient();
69 $client->request('GET', '/api/salts/admin.json');
70 $salt = json_decode($client->getResponse()->getContent());
71
72 $headers = $this->generateHeaders('admin', 'mypassword', $salt[0]);
73
74 $entry = $client->getContainer()
75 ->get('doctrine.orm.entity_manager')
76 ->getRepository('WallabagCoreBundle:Entry')
77 ->findOneByIsArchived(false);
78
79 if (!$entry) {
80 $this->markTestSkipped('No content found in db.');
81 }
82
83 $client->request('GET', '/api/entries/'.$entry->getId().'.json', array(), array(), $headers);
84 $this->assertContains($entry->getTitle(), $client->getResponse()->getContent());
85
86 $this->assertTrue(
87 $client->getResponse()->headers->contains(
88 'Content-Type',
89 'application/json'
90 )
91 );
92 }
93
94 public function testGetEntries()
95 {
96 $client = $this->createClient();
97 $client->request('GET', '/api/salts/admin.json');
98 $salt = json_decode($client->getResponse()->getContent());
99
100 $headers = $this->generateHeaders('admin', 'mypassword', $salt[0]);
101
102 $client->request('GET', '/api/entries', array(), array(), $headers);
103
104 $this->assertEquals(200, $client->getResponse()->getStatusCode());
105
106 $this->assertGreaterThanOrEqual(1, count(json_decode($client->getResponse()->getContent())));
107
108 $this->assertContains('Google', $client->getResponse()->getContent());
109
110 $this->assertTrue(
111 $client->getResponse()->headers->contains(
112 'Content-Type',
113 'application/json'
114 )
115 );
116 }
117
118 public function testDeleteEntry()
119 {
120 $client = $this->createClient();
121 $client->request('GET', '/api/salts/admin.json');
122 $salt = json_decode($client->getResponse()->getContent());
123
124 $headers = $this->generateHeaders('admin', 'mypassword', $salt[0]);
125
126 $entry = $client->getContainer()
127 ->get('doctrine.orm.entity_manager')
128 ->getRepository('WallabagCoreBundle:Entry')
129 ->findOneByUser(1);
130
131 if (!$entry) {
132 $this->markTestSkipped('No content found in db.');
133 }
134
135 $client->request('DELETE', '/api/entries/'.$entry->getId().'.json', array(), array(), $headers);
136
137 $this->assertEquals(200, $client->getResponse()->getStatusCode());
138
139 // We'll try to delete this entry again
140 $client->request('GET', '/api/salts/admin.json');
141 $salt = json_decode($client->getResponse()->getContent());
142
143 $headers = $this->generateHeaders('admin', 'mypassword', $salt[0]);
144
145 $client->request('DELETE', '/api/entries/'.$entry->getId().'.json', array(), array(), $headers);
146
147 $this->assertEquals(404, $client->getResponse()->getStatusCode());
148 }
149
150 public function testGetTagsEntry()
151 {
152 $client = $this->createClient();
153 $client->request('GET', '/api/salts/admin.json');
154 $salt = json_decode($client->getResponse()->getContent());
155 $headers = $this->generateHeaders('admin', 'mypassword', $salt[0]);
156
157 $entry = $client->getContainer()
158 ->get('doctrine.orm.entity_manager')
159 ->getRepository('WallabagCoreBundle:Entry')
160 ->findOneWithTags(1);
161
162 $entry = $entry[0];
163
164 if (!$entry) {
165 $this->markTestSkipped('No content found in db.');
166 }
167
168 $tags = array();
169 foreach ($entry->getTags() as $tag) {
170 $tags[] = array('id' => $tag->getId(), 'label' => $tag->getLabel());
171 }
172
173 $client->request('GET', '/api/entries/'.$entry->getId().'/tags', array(), array(), $headers);
174
175 $this->assertEquals(json_encode($tags, JSON_HEX_QUOT), $client->getResponse()->getContent());
176 }
177
178 public function testPostTagsOnEntry()
179 {
180 $client = $this->createClient();
181 $client->request('GET', '/api/salts/admin.json');
182 $salt = json_decode($client->getResponse()->getContent());
183 $headers = $this->generateHeaders('admin', 'mypassword', $salt[0]);
184
185 $entry = $client->getContainer()
186 ->get('doctrine.orm.entity_manager')
187 ->getRepository('WallabagCoreBundle:Entry')
188 ->findOneByUser(1);
189
190 if (!$entry) {
191 $this->markTestSkipped('No content found in db.');
192 }
193
194 $newTags = 'tag1,tag2,tag3';
195
196 $client->request('POST', '/api/entries/'.$entry->getId().'/tags', array('tags' => $newTags), array(), $headers);
197
198 $this->assertEquals(200, $client->getResponse()->getStatusCode());
199
200 $entryDB = $client->getContainer()
201 ->get('doctrine.orm.entity_manager')
202 ->getRepository('WallabagCoreBundle:Entry')
203 ->find($entry->getId());
204
205 $tagsInDB = array();
206 foreach ($entryDB->getTags()->toArray() as $tag) {
207 $tagsInDB[$tag->getId()] = $tag->getLabel();
208 }
209
210 foreach (explode(',', $newTags) as $tag) {
211 $this->assertContains($tag, $tagsInDB);
212 }
213 }
214}
diff --git a/src/Wallabag/CoreBundle/Tests/WallabagTestCase.php b/src/Wallabag/CoreBundle/Tests/WallabagCoreTestCase.php
index 22016d8e..e5096528 100644
--- a/src/Wallabag/CoreBundle/Tests/WallabagTestCase.php
+++ b/src/Wallabag/CoreBundle/Tests/WallabagCoreTestCase.php
@@ -4,7 +4,7 @@ namespace Wallabag\CoreBundle\Tests;
4 4
5use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; 5use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
6 6
7abstract class WallabagTestCase extends WebTestCase 7abstract class WallabagCoreTestCase extends WebTestCase
8{ 8{
9 private $client = null; 9 private $client = null;
10 10
diff --git a/src/Wallabag/CoreBundle/WallabagCoreBundle.php b/src/Wallabag/CoreBundle/WallabagCoreBundle.php
index 1deab03a..f5899e39 100644
--- a/src/Wallabag/CoreBundle/WallabagCoreBundle.php
+++ b/src/Wallabag/CoreBundle/WallabagCoreBundle.php
@@ -3,16 +3,7 @@
3namespace Wallabag\CoreBundle; 3namespace Wallabag\CoreBundle;
4 4
5use Symfony\Component\HttpKernel\Bundle\Bundle; 5use Symfony\Component\HttpKernel\Bundle\Bundle;
6use Wallabag\CoreBundle\DependencyInjection\Security\Factory\WsseFactory;
7use Symfony\Component\DependencyInjection\ContainerBuilder;
8 6
9class WallabagCoreBundle extends Bundle 7class WallabagCoreBundle extends Bundle
10{ 8{
11 public function build(ContainerBuilder $container)
12 {
13 parent::build($container);
14
15 $extension = $container->getExtension('security');
16 $extension->addSecurityListenerFactory(new WsseFactory());
17 }
18} 9}