2013-03-06 27 views
11

Sto tentando di implementare una funzionalità di sicurezza in un progetto Symfony 2.1 in cui l'amministratore può creare un utente con una password iniziale, quindi quando l'utente esegue il login per la prima volta il gestore della password di modifica viene attivato automaticamente.Come forzare la modifica della password utilizzando FOSUserBundle?

Sto incontrando problemi che sovrascrivono le classi FOSUserBundle e sto pensando che sicuramente questo è già stato costruito in qualche modo, almeno in parte, anche se non riesco a vederlo nei documenti ovunque.

Vorrei utilizzare il flag credentials_expired nell'entità. Quando l'amministratore crea l'utente, questo sarà impostato su 1. Quando l'utente accede per la prima volta, credentials_expired è selezionato e invece di generare un'eccezione, viene attivata la modifica della password. L'ho fatto fino a qui.

ChangePasswordController dovrebbe quindi assicurarsi che la password sia effettivamente cambiata (questo non sembra il comportamento predefinito in FOS) e credentials_expired è impostato su 0. Questo è il punto in cui sono bloccato. Ci sono così tanti livelli di servizi che non riesco a ottenere le cose personalizzate correttamente.

+0

Potrebbe essere più specifico? Ci sono molti approcci per risolverlo. Pubblicare il tuo codice ci aiuterà a identificare come procedere. Ad ogni modo, usare ruoli più probabili dei flag, potrebbe essere una buona idea, perché puoi gestirlo con il firewall di symfony. Pertanto, le persone che hanno il ruolo CREDENTIAL_EXPIRED, non possono accedere all'intero web e sono bloccate in un modulo che le costringe a cambiare la password. – Manu

+0

Il codice sarebbe FOSUserBundle. Il ruolo è una grande idea in quanto non richiederebbe l'estensione delle lezioni. Darò un colpo. Grazie. – David

risposta

12

Ecco la risposta dettagliata. Grazie Manu per il trampolino di lancio!

prima cosa, assicurarsi di ottenere il corretto FOSUserBundle nel file composer.json ("dev-master", non "*"):

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

Il seguente è tutto contenuto nel mio fagotto utente, che estende FOSUserBundle come indicato nel documento di installazione.

PortalFlare/Bundle/UserBundle/Resources/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)); 
    } 
} 

Il problema con FOSUserBundle che in realtà non si assicura che l'utente cambi la password è un problema per un altro giorno.

Spero che questo aiuti qualcuno.

4

Un buon approccio potrebbe essere iniziare a definire un ROLE_USER che sarà il ruolo che ha accesso all'intera app. Quando l'utente si registra, aggiunge automaticamente ROLE_CREDENTIAL_EXPIRED o qualcosa del genere. Utilizzando JMSSecurityExtraBundle, è possibile utilizzare le annotazioni nel controller, decidendo se gli utenti con un determinato ruolo possono accedere. Controlla anche i documenti su come Symfony handle the HTTP Authentication.

+0

Povero @Manu merita un upvote Penso :) –

+0

Questo è molto gentile da parte tua! Grazie @DarraghEnright :) – Manu

Problemi correlati