diff options
author | Jeremy <j0k3r@users.noreply.github.com> | 2015-02-10 13:49:57 +0100 |
---|---|---|
committer | Jeremy <j0k3r@users.noreply.github.com> | 2015-02-10 13:49:57 +0100 |
commit | 2c0ffcf3972e2f58267b805a26835f452e016761 (patch) | |
tree | b1146d6a9b98bb2ce238fff10fed4a1bf872c4b4 /src | |
parent | cbce162b407024882d8c37a7e3298c85175d2651 (diff) | |
parent | 92504e0dd489c0d11abc87bee42ffca717db0480 (diff) | |
download | wallabag-2c0ffcf3972e2f58267b805a26835f452e016761.tar.gz wallabag-2c0ffcf3972e2f58267b805a26835f452e016761.tar.zst wallabag-2c0ffcf3972e2f58267b805a26835f452e016761.zip |
Merge pull request #1068 from wallabag/v2-api-authentication
V2 api authentication
Diffstat (limited to 'src')
9 files changed, 83 insertions, 52 deletions
diff --git a/src/Wallabag/CoreBundle/Controller/EntryController.php b/src/Wallabag/CoreBundle/Controller/EntryController.php index e0697ca3..5378486a 100644 --- a/src/Wallabag/CoreBundle/Controller/EntryController.php +++ b/src/Wallabag/CoreBundle/Controller/EntryController.php | |||
@@ -6,7 +6,6 @@ use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; | |||
6 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; | 6 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; |
7 | use Symfony\Component\HttpFoundation\Request; | 7 | use Symfony\Component\HttpFoundation\Request; |
8 | use Wallabag\CoreBundle\Entity\Entry; | 8 | use Wallabag\CoreBundle\Entity\Entry; |
9 | use Wallabag\CoreBundle\Repository; | ||
10 | use Wallabag\CoreBundle\Service\Extractor; | 9 | use Wallabag\CoreBundle\Service\Extractor; |
11 | use Wallabag\CoreBundle\Helper\Url; | 10 | use Wallabag\CoreBundle\Helper\Url; |
12 | 11 | ||
diff --git a/src/Wallabag/CoreBundle/Controller/WallabagRestController.php b/src/Wallabag/CoreBundle/Controller/WallabagRestController.php index c298d849..f77a3749 100644 --- a/src/Wallabag/CoreBundle/Controller/WallabagRestController.php +++ b/src/Wallabag/CoreBundle/Controller/WallabagRestController.php | |||
@@ -13,6 +13,29 @@ use Wallabag\CoreBundle\Service\Extractor; | |||
13 | class WallabagRestController extends Controller | 13 | class WallabagRestController extends Controller |
14 | { | 14 | { |
15 | /** | 15 | /** |
16 | * Retrieve salt for a giver user. | ||
17 | * | ||
18 | * @ApiDoc( | ||
19 | * parameters={ | ||
20 | * {"name"="username", "dataType"="string", "required"=true, "description"="username"} | ||
21 | * } | ||
22 | * ) | ||
23 | * @return string | ||
24 | */ | ||
25 | public function getSaltAction($username) | ||
26 | { | ||
27 | $user = $this | ||
28 | ->getDoctrine() | ||
29 | ->getRepository('WallabagCoreBundle:User') | ||
30 | ->findOneByUsername($username); | ||
31 | |||
32 | if (is_null($user)) { | ||
33 | throw $this->createNotFoundException(); | ||
34 | } | ||
35 | |||
36 | return $user->getSalt(); | ||
37 | } | ||
38 | /** | ||
16 | * Retrieve all entries. It could be filtered by many options. | 39 | * Retrieve all entries. It could be filtered by many options. |
17 | * | 40 | * |
18 | * @ApiDoc( | 41 | * @ApiDoc( |
@@ -43,7 +66,7 @@ class WallabagRestController extends Controller | |||
43 | $entries = $this | 66 | $entries = $this |
44 | ->getDoctrine() | 67 | ->getDoctrine() |
45 | ->getRepository('WallabagCoreBundle:Entry') | 68 | ->getRepository('WallabagCoreBundle:Entry') |
46 | ->findEntries(1, $isArchived, $isStarred, $isDeleted, $sort, $order); | 69 | ->findEntries($this->getUser()->getId(), $isArchived, $isStarred, $isDeleted, $sort, $order); |
47 | 70 | ||
48 | if (!is_array($entries)) { | 71 | if (!is_array($entries)) { |
49 | throw $this->createNotFoundException(); | 72 | throw $this->createNotFoundException(); |
@@ -85,8 +108,7 @@ class WallabagRestController extends Controller | |||
85 | $url = $request->request->get('url'); | 108 | $url = $request->request->get('url'); |
86 | 109 | ||
87 | $content = Extractor::extract($url); | 110 | $content = Extractor::extract($url); |
88 | $entry = new Entry(); | 111 | $entry = new Entry($this->getUser()); |
89 | $entry->setUserId(1); | ||
90 | $entry->setUrl($url); | 112 | $entry->setUrl($url); |
91 | $entry->setTitle($request->request->get('title') ?: $content->getTitle()); | 113 | $entry->setTitle($request->request->get('title') ?: $content->getTitle()); |
92 | $entry->setContent($content->getBody()); | 114 | $entry->setContent($content->getBody()); |
diff --git a/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php b/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php index 7cc4165e..c6ecc99e 100644 --- a/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php +++ b/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php | |||
@@ -3,7 +3,7 @@ | |||
3 | namespace Wallabag\CoreBundle\DependencyInjection; | 3 | namespace Wallabag\CoreBundle\DependencyInjection; |
4 | 4 | ||
5 | use Symfony\Component\DependencyInjection\ContainerBuilder; | 5 | use Symfony\Component\DependencyInjection\ContainerBuilder; |
6 | use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; | 6 | use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; |
7 | use Symfony\Component\HttpKernel\DependencyInjection\Extension; | 7 | use Symfony\Component\HttpKernel\DependencyInjection\Extension; |
8 | use Symfony\Component\Config\FileLocator; | 8 | use Symfony\Component\Config\FileLocator; |
9 | 9 | ||
@@ -11,8 +11,8 @@ class 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 XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); | 14 | $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); |
15 | $loader->load('services.xml'); | 15 | $loader->load('services.yml'); |
16 | } | 16 | } |
17 | 17 | ||
18 | public function getAlias() | 18 | public function getAlias() |
diff --git a/src/Wallabag/CoreBundle/Repository/EntryRepository.php b/src/Wallabag/CoreBundle/Repository/EntryRepository.php index 5ae1337a..1805cf3f 100644 --- a/src/Wallabag/CoreBundle/Repository/EntryRepository.php +++ b/src/Wallabag/CoreBundle/Repository/EntryRepository.php | |||
@@ -91,12 +91,12 @@ class EntryRepository extends EntityRepository | |||
91 | /** | 91 | /** |
92 | * Find Entries | 92 | * Find Entries |
93 | * | 93 | * |
94 | * @param int $userId | 94 | * @param int $userId |
95 | * @param bool $isArchived | 95 | * @param bool $isArchived |
96 | * @param bool $isStarred | 96 | * @param bool $isStarred |
97 | * @param bool $isDeleted | 97 | * @param bool $isDeleted |
98 | * @param string $sort | 98 | * @param string $sort |
99 | * @param string $order | 99 | * @param string $order |
100 | * | 100 | * |
101 | * @return ArrayCollection | 101 | * @return ArrayCollection |
102 | */ | 102 | */ |
diff --git a/src/Wallabag/CoreBundle/Resources/config/services.xml b/src/Wallabag/CoreBundle/Resources/config/services.xml deleted file mode 100644 index 859665ca..00000000 --- a/src/Wallabag/CoreBundle/Resources/config/services.xml +++ /dev/null | |||
@@ -1,27 +0,0 @@ | |||
1 | <?xml version="1.0" ?> | ||
2 | |||
3 | <container xmlns="http://symfony.com/schema/dic/services" | ||
4 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
5 | xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> | ||
6 | |||
7 | <services> | ||
8 | <!-- Twig --> | ||
9 | <service id="wallabag_core.twig.wallabag" class="Wallabag\CoreBundle\Twig\Extension\WallabagExtension"> | ||
10 | <tag name="twig.extension" /> | ||
11 | </service> | ||
12 | |||
13 | <!-- Security --> | ||
14 | <service id="wsse.security.authentication.provider" | ||
15 | class="Wallabag\CoreBundle\Security\Authentication\Provider\WsseProvider" public="false"> | ||
16 | <argument /> <!-- User Provider --> | ||
17 | <argument>%kernel.cache_dir%/security/nonces</argument> | ||
18 | </service> | ||
19 | |||
20 | <service id="wsse.security.authentication.listener" | ||
21 | class="Wallabag\CoreBundle\Security\Firewall\WsseListener" public="false"> | ||
22 | <argument type="service" id="security.context"/> | ||
23 | <argument type="service" id="security.authentication.manager" /> | ||
24 | </service> | ||
25 | </services> | ||
26 | |||
27 | </container> | ||
diff --git a/src/Wallabag/CoreBundle/Resources/config/services.yml b/src/Wallabag/CoreBundle/Resources/config/services.yml new file mode 100644 index 00000000..b066c1a3 --- /dev/null +++ b/src/Wallabag/CoreBundle/Resources/config/services.yml | |||
@@ -0,0 +1,15 @@ | |||
1 | services: | ||
2 | wallabag_core.twig.wallabag: | ||
3 | class: Wallabag\CoreBundle\Twig\Extension\WallabagExtension | ||
4 | tags: | ||
5 | - { name: twig.extension } | ||
6 | wsse.security.authentication.provider: | ||
7 | class: Wallabag\CoreBundle\Security\Authentication\Provider\WsseProvider | ||
8 | public: false | ||
9 | arguments: ['', '%kernel.cache_dir%/security/nonces'] | ||
10 | wsse.security.authentication.listener: | ||
11 | class: Wallabag\CoreBundle\Security\Firewall\WsseListener | ||
12 | public: false | ||
13 | tags: | ||
14 | - { name: monolog.logger, channel: wsse } | ||
15 | arguments: ['@security.context', '@security.authentication.manager', '@logger'] | ||
diff --git a/src/Wallabag/CoreBundle/Security/Authentication/Provider/WsseProvider.php b/src/Wallabag/CoreBundle/Security/Authentication/Provider/WsseProvider.php index 5499f400..7e6a5dfb 100644 --- a/src/Wallabag/CoreBundle/Security/Authentication/Provider/WsseProvider.php +++ b/src/Wallabag/CoreBundle/Security/Authentication/Provider/WsseProvider.php | |||
@@ -17,12 +17,21 @@ class WsseProvider implements AuthenticationProviderInterface | |||
17 | { | 17 | { |
18 | $this->userProvider = $userProvider; | 18 | $this->userProvider = $userProvider; |
19 | $this->cacheDir = $cacheDir; | 19 | $this->cacheDir = $cacheDir; |
20 | |||
21 | // If cache directory does not exist we create it | ||
22 | if (!is_dir($this->cacheDir)) { | ||
23 | mkdir($this->cacheDir, 0777, true); | ||
24 | } | ||
20 | } | 25 | } |
21 | 26 | ||
22 | public function authenticate(TokenInterface $token) | 27 | public function authenticate(TokenInterface $token) |
23 | { | 28 | { |
24 | $user = $this->userProvider->loadUserByUsername($token->getUsername()); | 29 | $user = $this->userProvider->loadUserByUsername($token->getUsername()); |
25 | 30 | ||
31 | if (!$user) { | ||
32 | throw new AuthenticationException("Bad credentials. Did you forgot your username?"); | ||
33 | } | ||
34 | |||
26 | if ($user && $this->validateDigest($token->digest, $token->nonce, $token->created, $user->getPassword())) { | 35 | if ($user && $this->validateDigest($token->digest, $token->nonce, $token->created, $user->getPassword())) { |
27 | $authenticatedToken = new WsseUserToken($user->getRoles()); | 36 | $authenticatedToken = new WsseUserToken($user->getRoles()); |
28 | $authenticatedToken->setUser($user); | 37 | $authenticatedToken->setUser($user); |
@@ -35,20 +44,30 @@ class WsseProvider implements AuthenticationProviderInterface | |||
35 | 44 | ||
36 | protected function validateDigest($digest, $nonce, $created, $secret) | 45 | protected function validateDigest($digest, $nonce, $created, $secret) |
37 | { | 46 | { |
38 | // Expire le timestamp après 5 minutes | 47 | // Check created time is not in the future |
48 | if (strtotime($created) > time()) { | ||
49 | throw new AuthenticationException("Back to the future..."); | ||
50 | } | ||
51 | |||
52 | // Expire timestamp after 5 minutes | ||
39 | if (time() - strtotime($created) > 300) { | 53 | if (time() - strtotime($created) > 300) { |
40 | return false; | 54 | throw new AuthenticationException("Too late for this timestamp... Watch your watch."); |
41 | } | 55 | } |
42 | 56 | ||
43 | // Valide que le nonce est unique dans les 5 minutes | 57 | // Validate nonce is unique within 5 minutes |
44 | if (file_exists($this->cacheDir.'/'.$nonce) && file_get_contents($this->cacheDir.'/'.$nonce) + 300 > time()) { | 58 | if (file_exists($this->cacheDir.'/'.$nonce) && file_get_contents($this->cacheDir.'/'.$nonce) + 300 > time()) { |
45 | throw new NonceExpiredException('Previously used nonce detected'); | 59 | throw new NonceExpiredException('Previously used nonce detected'); |
46 | } | 60 | } |
61 | |||
47 | file_put_contents($this->cacheDir.'/'.$nonce, time()); | 62 | file_put_contents($this->cacheDir.'/'.$nonce, time()); |
48 | 63 | ||
49 | // Valide le Secret | 64 | // Validate Secret |
50 | $expected = base64_encode(sha1(base64_decode($nonce).$created.$secret, true)); | 65 | $expected = base64_encode(sha1(base64_decode($nonce).$created.$secret, true)); |
51 | 66 | ||
67 | if ($digest !== $expected) { | ||
68 | throw new AuthenticationException("Bad credentials ! Digest is not as expected."); | ||
69 | } | ||
70 | |||
52 | return $digest === $expected; | 71 | return $digest === $expected; |
53 | } | 72 | } |
54 | 73 | ||
diff --git a/src/Wallabag/CoreBundle/Security/Firewall/WsseListener.php b/src/Wallabag/CoreBundle/Security/Firewall/WsseListener.php index 4d4f2145..6ffdfaf0 100644 --- a/src/Wallabag/CoreBundle/Security/Firewall/WsseListener.php +++ b/src/Wallabag/CoreBundle/Security/Firewall/WsseListener.php | |||
@@ -9,16 +9,19 @@ use Symfony\Component\Security\Core\Exception\AuthenticationException; | |||
9 | use Symfony\Component\Security\Core\SecurityContextInterface; | 9 | use Symfony\Component\Security\Core\SecurityContextInterface; |
10 | use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; | 10 | use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; |
11 | use Wallabag\CoreBundle\Security\Authentication\Token\WsseUserToken; | 11 | use Wallabag\CoreBundle\Security\Authentication\Token\WsseUserToken; |
12 | use Psr\Log\LoggerInterface; | ||
12 | 13 | ||
13 | class WsseListener implements ListenerInterface | 14 | class WsseListener implements ListenerInterface |
14 | { | 15 | { |
15 | protected $securityContext; | 16 | protected $securityContext; |
16 | protected $authenticationManager; | 17 | protected $authenticationManager; |
18 | protected $logger; | ||
17 | 19 | ||
18 | public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager) | 20 | public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, LoggerInterface $logger) |
19 | { | 21 | { |
20 | $this->securityContext = $securityContext; | 22 | $this->securityContext = $securityContext; |
21 | $this->authenticationManager = $authenticationManager; | 23 | $this->authenticationManager = $authenticationManager; |
24 | $this->logger = $logger; | ||
22 | } | 25 | } |
23 | 26 | ||
24 | public function handle(GetResponseEvent $event) | 27 | public function handle(GetResponseEvent $event) |
@@ -41,17 +44,19 @@ class WsseListener implements ListenerInterface | |||
41 | $authToken = $this->authenticationManager->authenticate($token); | 44 | $authToken = $this->authenticationManager->authenticate($token); |
42 | 45 | ||
43 | $this->securityContext->setToken($authToken); | 46 | $this->securityContext->setToken($authToken); |
44 | } catch (AuthenticationException $failed) { | ||
45 | // ... you might log something here | ||
46 | 47 | ||
47 | // To deny the authentication clear the token. This will redirect to the login page. | 48 | return; |
48 | // $this->securityContext->setToken(null); | 49 | } catch (AuthenticationException $failed) { |
49 | // return; | 50 | $failedMessage = 'WSSE Login failed for '.$token->getUsername().'. Why ? '.$failed->getMessage(); |
51 | $this->logger->err($failedMessage); | ||
50 | 52 | ||
51 | // Deny authentication with a '403 Forbidden' HTTP response | 53 | // Deny authentication with a '403 Forbidden' HTTP response |
52 | $response = new Response(); | 54 | $response = new Response(); |
53 | $response->setStatusCode(403); | 55 | $response->setStatusCode(403); |
56 | $response->setContent($failedMessage); | ||
54 | $event->setResponse($response); | 57 | $event->setResponse($response); |
58 | |||
59 | return; | ||
55 | } | 60 | } |
56 | } | 61 | } |
57 | } | 62 | } |
diff --git a/src/Wallabag/CoreBundle/Tests/WallabagTestCase.php b/src/Wallabag/CoreBundle/Tests/WallabagTestCase.php index 5f092318..edc7d992 100644 --- a/src/Wallabag/CoreBundle/Tests/WallabagTestCase.php +++ b/src/Wallabag/CoreBundle/Tests/WallabagTestCase.php | |||
@@ -3,8 +3,6 @@ | |||
3 | namespace Wallabag\CoreBundle\Tests; | 3 | namespace Wallabag\CoreBundle\Tests; |
4 | 4 | ||
5 | use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; | 5 | use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; |
6 | use Symfony\Component\BrowserKit\Cookie; | ||
7 | use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; | ||
8 | 6 | ||
9 | class WallabagTestCase extends WebTestCase | 7 | class WallabagTestCase extends WebTestCase |
10 | { | 8 | { |