]> git.immae.eu Git - github/wallabag/wallabag.git/commitdiff
first implementation of security
authorNicolas Lœuillet <nicolas@loeuillet.org>
Sat, 31 Jan 2015 14:14:10 +0000 (15:14 +0100)
committerNicolas Lœuillet <nicolas@loeuillet.org>
Sat, 31 Jan 2015 14:14:10 +0000 (15:14 +0100)
18 files changed:
app/config/routing.yml
app/config/security.yml
src/Wallabag/CoreBundle/Controller/SecurityController.php [new file with mode: 0644]
src/Wallabag/CoreBundle/Controller/WallabagRestController.php
src/Wallabag/CoreBundle/DependencyInjection/Security/Factory/WsseFactory.php [new file with mode: 0644]
src/Wallabag/CoreBundle/Entity/Entries.php
src/Wallabag/CoreBundle/Entity/Users.php
src/Wallabag/CoreBundle/Helper/Entries.php [new file with mode: 0644]
src/Wallabag/CoreBundle/Repository/EntriesRepository.php
src/Wallabag/CoreBundle/Resources/config/services.xml
src/Wallabag/CoreBundle/Resources/views/Security/login.html.twig [new file with mode: 0644]
src/Wallabag/CoreBundle/Resources/views/_menu.html.twig
src/Wallabag/CoreBundle/Resources/views/layout-login.html.twig [new file with mode: 0644]
src/Wallabag/CoreBundle/Resources/views/layout.html.twig
src/Wallabag/CoreBundle/Security/Authentication/Provider/WsseProvider.php [new file with mode: 0644]
src/Wallabag/CoreBundle/Security/Authentication/Token/WsseUserToken.php [new file with mode: 0644]
src/Wallabag/CoreBundle/Security/Firewall/WsseListener.php [new file with mode: 0644]
src/Wallabag/CoreBundle/WallabagCoreBundle.php

index 8e04a0c817b787c275cdc6440184fbc93ca1f6e2..426dcdcfe8aff3c92f507c0dc196800e7d536ae7 100644 (file)
@@ -10,6 +10,14 @@ doc-api:
     resource: "@NelmioApiDocBundle/Resources/config/routing.yml"
     prefix: /api/doc
 
+login:
+    pattern:   /login
+    defaults:  { _controller: WallabagCoreBundle:Security:login }
+login_check:
+    pattern:   /login_check
+logout:
+    path:   /logout
+
 #wallabag_api:
 #    resource: "@WallabagApiBundle/Controller/"
 #    type:     annotation
index a28b1db99c050e15dcce20243a53dc418ab3e1a6..f4fefe2e4f24e550ddb4ec11305fd2cf7cdca8c0 100644 (file)
@@ -1,52 +1,58 @@
-# you can read more about security in the related section of the documentation
-# http://symfony.com/doc/current/book/security.html
 security:
-    # http://symfony.com/doc/current/book/security.html#encoding-the-user-s-password
     encoders:
-        Symfony\Component\Security\Core\User\User: plaintext
+        Wallabag\CoreBundle\Entity\Users:
+            algorithm:        sha1
+            encode_as_base64: false
+            iterations:       1
 
-    # http://symfony.com/doc/current/book/security.html#hierarchical-roles
     role_hierarchy:
         ROLE_ADMIN:       ROLE_USER
-        ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
+        ROLE_SUPER_ADMIN: [ ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH ]
 
-    # http://symfony.com/doc/current/book/security.html#where-do-users-come-from-user-providers
     providers:
-        in_memory:
-            memory:
-                users:
-                    user:  { password: userpass, roles: [ 'ROLE_USER' ] }
-                    admin: { password: adminpass, roles: [ 'ROLE_ADMIN' ] }
+        administrators:
+            entity: { class: WallabagCoreBundle:Users, property: username }
 
     # the main part of the security, where you can set up firewalls
     # for specific sections of your app
     firewalls:
-        # disables authentication for assets and the profiler, adapt it according to your needs
-        dev:
-            pattern:  ^/(_(profiler|wdt)|css|images|js)/
-            security: false
-        # the login page has to be accessible for everybody
-        demo_login:
-            pattern:  ^/demo/secured/login$
-            security: false
-
-        # secures part of the application
-        demo_secured_area:
-            pattern:    ^/demo/secured/
-            # it's important to notice that in this case _demo_security_check and _demo_login
-            # are route names and that they are specified in the AcmeDemoBundle
+        #wsse_secured:
+        #    pattern:   /api/.*
+        #    wsse:      true
+        login_firewall:
+            pattern:    ^/login$
+            anonymous:  ~
+
+        secured_area:
+            pattern:    ^/
+            anonymous: ~
             form_login:
-                check_path: _demo_security_check
-                login_path: _demo_login
+                login_path:                     /login
+
+                use_forward:                    false
+
+                check_path:                     /login_check
+
+                post_only:                      true
+
+                always_use_default_target_path: true
+                default_target_path:            /
+                target_path_parameter:          redirect_url
+                use_referer:                    true
+
+                failure_path:                   null
+                failure_forward:                false
+
+                username_parameter:             _username
+                password_parameter:             _password
+
+                csrf_parameter:                 _csrf_token
+                intention:                      authenticate
+
             logout:
-                path:   _demo_logout
-                target: _demo
-            #anonymous: ~
-            #http_basic:
-            #    realm: "Secured Demo Area"
-
-    # with these settings you can restrict or allow access for different parts
-    # of your application based on roles, ip, host or methods
-    # http://symfony.com/doc/current/cookbook/security/access_control.html
+                path:   /logout
+                target: /
+
     access_control:
-        #- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https }
\ No newline at end of file
+        - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
+        - { path: ^/, roles: ROLE_USER }
diff --git a/src/Wallabag/CoreBundle/Controller/SecurityController.php b/src/Wallabag/CoreBundle/Controller/SecurityController.php
new file mode 100644 (file)
index 0000000..51f9cc2
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+
+namespace Wallabag\CoreBundle\Controller;
+
+use Symfony\Bundle\FrameworkBundle\Controller\Controller;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Security\Core\SecurityContext;
+
+class SecurityController extends Controller
+{
+    public function loginAction(Request $request)
+    {
+        $session = $request->getSession();
+        // get the login error if there is one
+        if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
+            $error = $request->attributes->get(SecurityContext::AUTHENTICATION_ERROR);
+        } else {
+            $error = $session->get(SecurityContext::AUTHENTICATION_ERROR);
+            $session->remove(SecurityContext::AUTHENTICATION_ERROR);
+        }
+        return $this->render('WallabagCoreBundle:Security:login.html.twig', array(
+            // last username entered by the user
+            'last_username' => $session->get(SecurityContext::LAST_USERNAME),
+            'error'         => $error,
+        ));
+    }
+}
\ No newline at end of file
index a6c0db37a672a7683b370079566d832ccc984904..8e018e88bce675cb4b15ca10e23645396e361b6a 100644 (file)
@@ -82,17 +82,18 @@ class WallabagRestController extends Controller
      */
     public function postEntriesAction(Request $request)
     {
-        //TODO la récup ne marche
+        //TODO la récup ne marche pas
         //TODO gérer si on passe le titre
         //TODO gérer si on passe les tags
         //TODO ne pas avoir du code comme ça qui doit se trouver dans le Repository
+        $url = $request->request->get('url');
+
+        $content = Extractor::extract($url);
         $entry = new Entries();
         $entry->setUserId(1);
-        $content = Extractor::extract($request->request->get('url'));
-
+        $entry->setUrl($url);
         $entry->setTitle($content->getTitle());
         $entry->setContent($content->getBody());
-
         $em = $this->getDoctrine()->getManager();
         $em->persist($entry);
         $em->flush();
diff --git a/src/Wallabag/CoreBundle/DependencyInjection/Security/Factory/WsseFactory.php b/src/Wallabag/CoreBundle/DependencyInjection/Security/Factory/WsseFactory.php
new file mode 100644 (file)
index 0000000..9807fe9
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+
+namespace Wallabag\CoreBundle\DependencyInjection\Security\Factory;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\DependencyInjection\DefinitionDecorator;
+use Symfony\Component\Config\Definition\Builder\NodeDefinition;
+use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface;
+
+class WsseFactory implements SecurityFactoryInterface
+{
+    public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
+    {
+        $providerId = 'security.authentication.provider.wsse.'.$id;
+        $container
+            ->setDefinition($providerId, new DefinitionDecorator('wsse.security.authentication.provider'))
+            ->replaceArgument(0, new Reference($userProvider))
+        ;
+
+        $listenerId = 'security.authentication.listener.wsse.'.$id;
+        $listener = $container->setDefinition($listenerId, new DefinitionDecorator('wsse.security.authentication.listener'));
+
+        return array($providerId, $listenerId, $defaultEntryPoint);
+    }
+
+    public function getPosition()
+    {
+        return 'pre_auth';
+    }
+
+    public function getKey()
+    {
+        return 'wsse';
+    }
+
+    public function addConfiguration(NodeDefinition $node)
+    {
+    }
+}
\ No newline at end of file
index 712ff1262097d82404884287c505479417cc553d..3c061a37bc5f1dad223b6344d484f92462521bb2 100644 (file)
@@ -10,6 +10,7 @@ use Symfony\Component\Validator\Constraints as Assert;
  *
  * @ORM\Entity(repositoryClass="Wallabag\CoreBundle\Repository\EntriesRepository")
  * @ORM\Table(name="entries")
+ *
  */
 class Entries
 {
index 3db4a3fd954c770755601539026c9bfc2c737b22..96867bd612f1d3b993a4749a9fb5f8394f403fbd 100644 (file)
@@ -3,6 +3,9 @@
 namespace Wallabag\CoreBundle\Entity;
 
 use Doctrine\ORM\Mapping as ORM;
+use Symfony\Component\Security\Core\User\UserInterface;
+use Symfony\Component\Security\Core\User\EquatableInterface;
+use Symfony\Component\Security\Core\User\AdvancedUserInterface;
 
 /**
  * Users
@@ -10,7 +13,7 @@ use Doctrine\ORM\Mapping as ORM;
  * @ORM\Table(name="users")
  * @ORM\Entity
  */
-class Users
+class Users implements AdvancedUserInterface, \Serializable
 {
     /**
      * @var integer
@@ -28,6 +31,11 @@ class Users
      */
     private $username;
 
+    /**
+     * @ORM\Column(type="string", length=32)
+     */
+    private $salt;
+
     /**
      * @var string
      *
@@ -49,7 +57,16 @@ class Users
      */
     private $email;
 
+    /**
+     * @ORM\Column(name="is_active", type="boolean")
+     */
+    private $isActive;
 
+    public function __construct()
+    {
+        $this->isActive = true;
+        $this->salt = md5(uniqid(null, true));
+    }
 
     /**
      * Get id
@@ -84,6 +101,22 @@ class Users
         return $this->username;
     }
 
+    /**
+     * @inheritDoc
+     */
+    public function getSalt()
+    {
+        return $this->salt;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function getRoles()
+    {
+        return array('ROLE_USER');
+    }
+
     /**
      * Set password
      *
@@ -152,4 +185,56 @@ class Users
     {
         return $this->email;
     }
+
+    /**
+     * @inheritDoc
+     */
+    public function eraseCredentials()
+    {
+    }
+
+    /**
+     * @see \Serializable::serialize()
+     */
+    public function serialize()
+    {
+        return serialize(array(
+            $this->id,
+        ));
+    }
+
+    /**
+     * @see \Serializable::unserialize()
+     */
+    public function unserialize($serialized)
+    {
+        list (
+            $this->id,
+            ) = unserialize($serialized);
+    }
+
+    public function isEqualTo(UserInterface $user)
+    {
+        return $this->username === $user->getUsername();
+    }
+
+    public function isAccountNonExpired()
+    {
+        return true;
+    }
+
+    public function isAccountNonLocked()
+    {
+        return true;
+    }
+
+    public function isCredentialsNonExpired()
+    {
+        return true;
+    }
+
+    public function isEnabled()
+    {
+        return $this->isActive;
+    }
 }
diff --git a/src/Wallabag/CoreBundle/Helper/Entries.php b/src/Wallabag/CoreBundle/Helper/Entries.php
new file mode 100644 (file)
index 0000000..a54c3a7
--- /dev/null
@@ -0,0 +1,10 @@
+<?php
+
+namespace Wallabag\CoreBundle\Helper;
+
+
+class Entries {
+
+
+
+}
\ No newline at end of file
index edbb96b4ffd61af0d9a7c0ed114ed21f5ef30ffd..e63c67e2c3be44b5cb588b8adf153a822803fcaf 100644 (file)
@@ -5,6 +5,8 @@ namespace Wallabag\CoreBundle\Repository;
 use Doctrine\ORM\Query;
 use Doctrine\ORM\EntityRepository;
 use Doctrine\ORM\Tools\Pagination\Paginator;
+use Wallabag\CoreBundle\Entity\Entries;
+use Wallabag\CoreBundle\Service\Extractor;
 
 class EntriesRepository extends EntityRepository
 {
@@ -79,6 +81,7 @@ class EntriesRepository extends EntityRepository
 
     public function findEntries($userId, $isArchived, $isStarred, $isDeleted, $sort, $order)
     {
+        //TODO tous les paramètres ne sont pas utilisés, à corriger
         $qb = $this->createQueryBuilder('e')
             ->select('e')
             ->where('e.isFav =:isStarred')->setParameter('isStarred', $isStarred)
index 02308e6aabba623d2fc2c63d06867b3848734137..d5bc5cca5f864b5571e64c6876a017f2f31b4fe1 100644 (file)
@@ -5,12 +5,24 @@
     xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
 
     <services>
+        <!-- Twig -->
         <service id="wallabag_core.twig.wallabag" class="Wallabag\CoreBundle\Twig\Extension\WallabagExtension">
             <tag name="twig.extension" />
         </service>
-    </services>
 
+        <!-- Security -->
+        <service id="wsse.security.authentication.provider"
+                 class="Wallabag\CoreBundle\Security\Authentication\Provider\WsseProvider" public="false">
+            <argument /> <!-- User Provider -->
+            <argument>%kernel.cache_dir%/security/nonces</argument>
+        </service>
 
+        <service id="wsse.security.authentication.listener"
+                 class="Wallabag\CoreBundle\Security\Firewall\WsseListener" public="false">
+            <argument type="service" id="security.context"/>
+            <argument type="service" id="security.authentication.manager" />
+        </service>
+    </services>
 
 </container>
 
diff --git a/src/Wallabag/CoreBundle/Resources/views/Security/login.html.twig b/src/Wallabag/CoreBundle/Resources/views/Security/login.html.twig
new file mode 100644 (file)
index 0000000..2437e3b
--- /dev/null
@@ -0,0 +1,32 @@
+{% extends "WallabagCoreBundle::layout-login.html.twig" %}
+
+{% block title %}{% trans %}login to your wallabag{% endtrans %}{% endblock %}
+{% block content %}
+    {% if error %}
+        <div>{{ error.message }}</div>
+    {% endif %}
+
+        <form action="{{ path('login_check') }}" method="post" name="loginform">
+            <fieldset class="w500p center">
+                <h2 class="mbs txtcenter">{% trans %}Login to wallabag{% endtrans %}</h2>
+
+                <div class="row">
+                    <label class="col w150p" for="username">{% trans %}Username{% endtrans %}</label>
+                    <input type="text" id="username" name="_username" value="{{ last_username }}" />
+                </div>
+
+                <div class="row">
+                    <label class="col w150p" for="password">{% trans %}Password{% endtrans %}</label>
+                    <input type="password" id="password" name="_password" />
+                </div>
+            {#
+                Si vous voulez contrôler l'URL vers laquelle l'utilisateur est redirigé en cas de succès
+                (plus de détails ci-dessous)
+                <input type="hidden" name="_target_path" value="/account" />
+            #}
+                <div class="row mts txtcenter">
+                    <button type="submit">login</button>
+                </div>
+            </fieldset>
+        </form>
+{% endblock %}
index d4560e84b18b837bcdd5068216b7dda3156e54db..3065ce4461d2acdeec75e21fc25a217328bc21ee 100644 (file)
@@ -10,6 +10,6 @@
                 </li>
                 <li><a href="?view=config">{% trans %}config{% endtrans %}</a></li>
                 <li><a href={{ path('about') }}>{% trans %}about{% endtrans %}</a></li>
-                <li><a class="icon icon-power" href="?logout" title="{% trans %}logout{% endtrans %}">{% trans %}logout{% endtrans %}</a></li>
+                <li><a class="icon icon-power" href="{{ path('logout') }}" title="{% trans %}logout{% endtrans %}">{% trans %}logout{% endtrans %}</a></li>
             </ul>
 
diff --git a/src/Wallabag/CoreBundle/Resources/views/layout-login.html.twig b/src/Wallabag/CoreBundle/Resources/views/layout-login.html.twig
new file mode 100644 (file)
index 0000000..9c39ea9
--- /dev/null
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!--[if lte IE 6]><html class="no-js ie6 ie67 ie678" lang="en"><![endif]-->
+<!--[if lte IE 7]><html class="no-js ie7 ie67 ie678" lang="en"><![endif]-->
+<!--[if IE 8]><html class="no-js ie8 ie678" lang="en"><![endif]-->
+<!--[if gt IE 8]><html class="no-js" lang="en"><![endif]-->
+<html lang="en">
+    <head>
+        <meta name="viewport" content="initial-scale=1.0">
+        <meta charset="utf-8">
+        <!--[if IE]>
+        <meta http-equiv="X-UA-Compatible" content="IE=10">
+        <![endif]-->
+        <title>{% block title %}{% endblock %} - wallabag</title>
+        {% include "WallabagCoreBundle::_head.html.twig" %}
+    </head>
+    <body class="login">
+        {% include "WallabagCoreBundle::_top.html.twig" %}
+        <div id="main">
+            {% block menu %}{% endblock %}
+            <div id="content" class="w600p center">
+            {% block content %}{% endblock %}
+            </div>
+        </div>
+        {% include "WallabagCoreBundle::_footer.html.twig" %}
+    </body>
+</html>
\ No newline at end of file
index 83830a4ae841729f07078b7c652dce5417d9f1d4..1f1753a48e14bb692edacc41ea1ce31b6103d452 100644 (file)
@@ -4,30 +4,30 @@
 <!--[if IE 8]><html class="no-js ie8 ie678" lang="en"><![endif]-->
 <!--[if gt IE 8]><html class="no-js" lang="en"><![endif]-->
 <html lang="en">
-<head>
-    <meta name="viewport" content="initial-scale=1.0">
-    <meta charset="utf-8">
-    <!--[if IE]>
-    <meta http-equiv="X-UA-Compatible" content="IE=10">
-    <![endif]-->
-    <title>{% block title %}{% endblock %} - wallabag</title>
-    {% include "WallabagCoreBundle::_head.html.twig" %}
-    {% include "WallabagCoreBundle::_bookmarklet.html.twig" %}
-</head>
-<body>
-{% include "WallabagCoreBundle::_top.html.twig" %}
-<div id="main">
-    {% block menu %}{% endblock %}
-    {% block precontent %}{% endblock %}
-    {% for flashMessage in app.session.flashbag.get('notice') %}
-        <div class="flash-notice">
-            {{ flashMessage }}
+    <head>
+        <meta name="viewport" content="initial-scale=1.0">
+        <meta charset="utf-8">
+        <!--[if IE]>
+        <meta http-equiv="X-UA-Compatible" content="IE=10">
+        <![endif]-->
+        <title>{% block title %}{% endblock %} - wallabag</title>
+        {% include "WallabagCoreBundle::_head.html.twig" %}
+        {% include "WallabagCoreBundle::_bookmarklet.html.twig" %}
+    </head>
+    <body>
+    {% include "WallabagCoreBundle::_top.html.twig" %}
+    <div id="main">
+        {% block menu %}{% endblock %}
+        {% block precontent %}{% endblock %}
+        {% for flashMessage in app.session.flashbag.get('notice') %}
+            <div class="flash-notice">
+                {{ flashMessage }}
+            </div>
+        {% endfor %}
+        <div id="content" class="w600p center">
+            {% block content %}{% endblock %}
         </div>
-    {% endfor %}
-    <div id="content" class="w600p center">
-        {% block content %}{% endblock %}
     </div>
-</div>
-{% include "WallabagCoreBundle::_footer.html.twig" %}
-</body>
+    {% include "WallabagCoreBundle::_footer.html.twig" %}
+    </body>
 </html>
\ No newline at end of file
diff --git a/src/Wallabag/CoreBundle/Security/Authentication/Provider/WsseProvider.php b/src/Wallabag/CoreBundle/Security/Authentication/Provider/WsseProvider.php
new file mode 100644 (file)
index 0000000..ac57e27
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+namespace Wallabag\CoreBundle\Security\Authentication\Provider;
+
+use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
+use Symfony\Component\Security\Core\User\UserProviderInterface;
+use Symfony\Component\Security\Core\Exception\AuthenticationException;
+use Symfony\Component\Security\Core\Exception\NonceExpiredException;
+use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
+use Wallabag\CoreBundle\Security\Authentication\Token\WsseUserToken;
+
+class WsseProvider implements AuthenticationProviderInterface
+{
+    private $userProvider;
+    private $cacheDir;
+
+    public function __construct(UserProviderInterface $userProvider, $cacheDir)
+    {
+        $this->userProvider = $userProvider;
+        $this->cacheDir     = $cacheDir;
+    }
+
+    public function authenticate(TokenInterface $token)
+    {
+        $user = $this->userProvider->loadUserByUsername($token->getUsername());
+
+        if ($user && $this->validateDigest($token->digest, $token->nonce, $token->created, $user->getPassword())) {
+            $authenticatedToken = new WsseUserToken($user->getRoles());
+            $authenticatedToken->setUser($user);
+
+            return $authenticatedToken;
+        }
+
+        throw new AuthenticationException('The WSSE authentication failed.');
+    }
+
+    protected function validateDigest($digest, $nonce, $created, $secret)
+    {
+        // Expire le timestamp après 5 minutes
+        if (time() - strtotime($created) > 300) {
+            return false;
+        }
+
+        // Valide que le nonce est unique dans les 5 minutes
+        if (file_exists($this->cacheDir.'/'.$nonce) && file_get_contents($this->cacheDir.'/'.$nonce) + 300 > time()) {
+            throw new NonceExpiredException('Previously used nonce detected');
+        }
+        file_put_contents($this->cacheDir.'/'.$nonce, time());
+
+        // Valide le Secret
+        $expected = base64_encode(sha1(base64_decode($nonce).$created.$secret, true));
+
+        return $digest === $expected;
+    }
+
+    public function supports(TokenInterface $token)
+    {
+        return $token instanceof WsseUserToken;
+    }
+}
\ No newline at end of file
diff --git a/src/Wallabag/CoreBundle/Security/Authentication/Token/WsseUserToken.php b/src/Wallabag/CoreBundle/Security/Authentication/Token/WsseUserToken.php
new file mode 100644 (file)
index 0000000..b189e22
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+namespace Wallabag\CoreBundle\Security\Authentication\Token;
+
+use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;
+
+class WsseUserToken extends AbstractToken
+{
+    public $created;
+    public $digest;
+    public $nonce;
+
+    public function __construct(array $roles = array())
+    {
+        parent::__construct($roles);
+
+        $this->setAuthenticated(count($roles) > 0);
+    }
+
+    public function getCredentials()
+    {
+        return '';
+    }
+}
\ No newline at end of file
diff --git a/src/Wallabag/CoreBundle/Security/Firewall/WsseListener.php b/src/Wallabag/CoreBundle/Security/Firewall/WsseListener.php
new file mode 100644 (file)
index 0000000..b247478
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+
+namespace Wallabag\CoreBundle\Security\Firewall;
+
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\Security\Http\Firewall\ListenerInterface;
+use Symfony\Component\Security\Core\Exception\AuthenticationException;
+use Symfony\Component\Security\Core\SecurityContextInterface;
+use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
+use Wallabag\CoreBundle\Security\Authentication\Token\WsseUserToken;
+
+class WsseListener implements ListenerInterface
+{
+    protected $securityContext;
+    protected $authenticationManager;
+
+    public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager)
+    {
+        $this->securityContext = $securityContext;
+        $this->authenticationManager = $authenticationManager;
+    }
+
+    public function handle(GetResponseEvent $event)
+    {
+        $request = $event->getRequest();
+
+        $wsseRegex = '/UsernameToken Username="([^"]+)", PasswordDigest="([^"]+)", Nonce="([^"]+)", Created="([^"]+)"/';
+        if (!$request->headers->has('x-wsse') || 1 !== preg_match($wsseRegex, $request->headers->get('x-wsse'), $matches)) {
+            return;
+        }
+
+        $token = new WsseUserToken();
+        $token->setUser($matches[1]);
+
+        $token->digest   = $matches[2];
+        $token->nonce    = $matches[3];
+        $token->created  = $matches[4];
+
+        try {
+            $authToken = $this->authenticationManager->authenticate($token);
+
+            $this->securityContext->setToken($authToken);
+        } catch (AuthenticationException $failed) {
+            // ... you might log something here
+
+            // To deny the authentication clear the token. This will redirect to the login page.
+            // $this->securityContext->setToken(null);
+            // return;
+
+            // Deny authentication with a '403 Forbidden' HTTP response
+            $response = new Response();
+            $response->setStatusCode(403);
+            $event->setResponse($response);
+
+        }
+    }
+}
\ No newline at end of file
index f5899e39c97a12f913f2eb23a26c8007d919ae94..1deab03a9cf373c705b8cf6db124ff18cd1200c7 100644 (file)
@@ -3,7 +3,16 @@
 namespace Wallabag\CoreBundle;
 
 use Symfony\Component\HttpKernel\Bundle\Bundle;
+use Wallabag\CoreBundle\DependencyInjection\Security\Factory\WsseFactory;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
 
 class WallabagCoreBundle extends Bundle
 {
+    public function build(ContainerBuilder $container)
+    {
+        parent::build($container);
+
+        $extension = $container->getExtension('security');
+        $extension->addSecurityListenerFactory(new WsseFactory());
+    }
 }