2013-03-06 15 views
11

Ich versuche, ein Sicherheitsfeature in einem Symfony 2.1-Projekt zu implementieren, in dem der Administrator einen Benutzer mit einem anfänglichen Kennwort erstellen kann, und wenn der Benutzer sich das erste Mal anmeldet, wird der Änderungspasswort-Handler automatisch ausgelöst.Wie erzwinge Passwortänderung mit FOSUserBundle?

Ich habe Probleme, die FOSUserBundle-Klassen zu umgehen, und ich denke, dass dies sicherlich schon irgendwie eingebaut ist, zumindest teilweise, obwohl ich es nirgendwo in der Dokumentation sehen kann.

Ich möchte das credentides_expired Flag in der Entität verwenden. Wenn der Administrator den Benutzer erstellt, wird dieser Wert auf 1 festgelegt. Wenn sich der Benutzer zum ersten Mal anmeldet, wird credentials_expired aktiviert, und statt eine Ausnahme auszulösen, wird change-password ausgelöst. Ich habe es bis hierher geschafft.

ChangePasswordController würde dann sicherstellen, dass das Passwort tatsächlich geändert wurde (dies scheint nicht wie das Standardverhalten in FOS) und die credentials_expired ist auf 0 gesetzt. Hier bin ich stecken. Es gibt so viele Ebenen von Dienstleistungen, dass ich die Dinge scheinbar nicht richtig anpassen kann.

+0

Konnten Sie genauer sein? Es gibt viele Ansätze, um dies zu lösen. Durch die Eingabe Ihres Codes können wir ermitteln, wie weiter verfahren werden soll. Wie auch immer, Rollen mit höherer Wahrscheinlichkeit als Flags zu verwenden, könnte eine gute Idee sein, weil Sie es mit der Symfony Firewall verwalten können. Daher können Personen, die die Rolle CREDENTIAL_EXPIRED haben, nicht auf das gesamte Web zugreifen, und sie sind in einem Formular stecken geblieben, das sie zwingt, ihr Kennwort zu ändern. – Manu

+0

Der Code wäre der FOSUserBundle. Die Rolle ist eine großartige Idee, da keine Klassen erweitert werden müssen. Ich gebe das eine Chance. Vielen Dank. – David

Antwort

12

Hier ist die detaillierte Antwort. Danke Manu für das Sprungbrett!

Erstens, stellen Sie sicher, dass die richtige FOSUserBundle in der composer.json-Datei ("dev-master", NICHT "*") zu erhalten:

"friendsofsymfony/user-bundle":"dev-master" 

Das Folgende ist alles in meinem eigenen Benutzer Bündel enthalten ist, Das erweitert den FOSUserBundle wie im Installationsdokument beschrieben.

PortalFlare/Bundle/UserBundle/Ressourcen/config/services.xml:

<container xmlns="http://symfony.com/schema/dic/services" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> 

<parameters> 
    <parameter key="portal_flare_user.forcepasswordchange.class">PortalFlare\Bundle\UserBundle\EventListener\ForcePasswordChange</parameter> 
    <parameter key="portal_flare_user.passwordchangelistener.class">PortalFlare\Bundle\UserBundle\EventListener\PasswordChangeListener</parameter> 
</parameters> 

<services> 
    <service id="portal_flare_user.forcepasswordchange" class="%portal_flare_user.forcepasswordchange.class%"> 
     <argument type="service" id="router" /> 
     <argument type="service" id="security.context" /> 
     <argument type="service" id="session" /> 
     <tag name="kernel.event_listener" event="kernel.request" method="onCheckStatus" priority="1" /> 
    </service> 
    <service id="portal_flare_user.passwordchange" class="%portal_flare_user.passwordchangelistener.class%"> 
     <argument type="service" id="router" /> 
     <argument type="service" id="security.context" /> 
     <argument type="service" id="fos_user.user_manager" /> 
     <tag name="kernel.event_subscriber" /> 
    </service> 
</services> 

</container> 

PortalFlare/Bundle/UserBundle/Eventlistener/ForcePasswordChange.php:

<?php 

namespace PortalFlare\Bundle\UserBundle\EventListener; 

use Symfony\Component\HttpFoundation\RedirectResponse; 
use Symfony\Component\HttpKernel\Event\GetResponseEvent; 

use Symfony\Component\Security\Core\SecurityContext; 
use Symfony\Bundle\FrameworkBundle\Routing\Router; 
use Symfony\Component\HttpFoundation\Session\Session; 

/** 
* @Service("request.set_messages_count_listener") 
* 
*/ 
class ForcePasswordChange { 

    private $security_context; 
    private $router; 
    private $session; 

    public function __construct(Router $router, SecurityContext $security_context, Session $session) { 
    $this->security_context = $security_context; 
    $this->router   = $router; 
    $this->session   = $session; 

    } 

    public function onCheckStatus(GetResponseEvent $event) { 

    if (($this->security_context->getToken()) && ($this->security_context->isGranted('IS_AUTHENTICATED_FULLY'))) { 

     $route_name = $event->getRequest()->get('_route'); 

     if ($route_name != 'fos_user_change_password') { 

     if ($this->security_context->getToken()->getUser()->hasRole('ROLE_FORCEPASSWORDCHANGE')) { 

      $response = new RedirectResponse($this->router->generate('fos_user_change_password')); 
      $this->session->setFlash('notice', "Your password has expired. Please change it."); 
      $event->setResponse($response); 

     } 

     } 

    } 

    } 

} 

PortalFlare/Bundle/UserBundle/EventListener/PasswordChangeListener.php:

<?php 
namespace PortalFlare\Bundle\UserBundle\EventListener; 

use FOS\UserBundle\FOSUserEvents; 
use FOS\UserBundle\Event\FormEvent; 
use FOS\UserBundle\Doctrine\UserManager; 
use Symfony\Component\EventDispatcher\EventSubscriberInterface; 
use Symfony\Component\HttpFoundation\RedirectResponse; 
use Symfony\Component\Routing\Generator\UrlGeneratorInterface; 
use Symfony\Component\Security\Core\SecurityContext; 

/** 
* Listener responsible to change the redirection at the end of the password change 
*/ 
class PasswordChangeListener implements EventSubscriberInterface { 
    private $security_context; 
    private $router; 
    private $usermanager; 

    public function __construct(UrlGeneratorInterface $router, SecurityContext $security_context, UserManager $usermanager) { 
    $this->security_context = $security_context; 
    $this->router   = $router; 
    $this->usermanager  = $usermanager; 
    } 

    /** 
    * {@inheritDoc} 
    */ 
    public static function getSubscribedEvents() { 
    return array(
     FOSUserEvents::CHANGE_PASSWORD_SUCCESS => 'onChangePasswordSuccess', 
    ); 
    } 

    public function onChangePasswordSuccess(FormEvent $event) { 

    $user = $this->security_context->getToken()->getUser(); 
    $user->removeRole('ROLE_FORCEPASSWORDCHANGE'); 
    $this->usermanager->updateUser($user); 

    $url = $this->router->generate('_welcome'); 
    $event->setResponse(new RedirectResponse($url)); 
    } 
} 

Das Problem, dass FOSUserBundle nicht wirklich dafür sorgt, dass der Benutzer das Passwort ändert, ist ein Problem für einen anderen Tag.

Ich hoffe, das hilft jemandem.

4

Ein guter Ansatz könnte sein, einen ROLE_USER zu definieren, der die Rolle hat, die Zugriff auf die gesamte App hat. Wenn der Benutzer sich registriert, fügt er ihm automatisch das ROLE_CREDENTIAL_EXPIRED oder etwas ähnliches hinzu. Unter Verwendung von JMSSecurityExtraBundle können Sie Anmerkungen in Ihrem Controller verwenden und entscheiden, ob Benutzer mit einer bestimmten Rolle darauf zugreifen können. Überprüfen Sie auch die Dokumentation, wie Symfony handle the HTTP Authentication.

+0

Schlechte @Manu verdient eine Verbesserung, denke ich :) –

+0

Das ist sehr nett von dir! Danke @DarraghEnright :) – Manu

Verwandte Themen