2013-03-06 12 views
11

Próbuję zaimplementować funkcję zabezpieczeń w projekcie Symfony 2.1, w którym administrator może utworzyć użytkownika z początkowym hasłem, a następnie, gdy użytkownik loguje się po raz pierwszy, procedura obsługi hasła zmiany jest uruchamiana automatycznie.Jak wymusić zmianę hasła za pomocą FOSUserBundle?

Występują problemy z nadpisaniem klas FOSUserBundle i myślę, że z pewnością jest to już zbudowane, przynajmniej w części, chociaż nie widzę go w dokumentach w dowolnym miejscu.

Chciałbym użyć flagi credentials_expired w encji. Gdy administrator utworzy użytkownika, zostanie on ustawiony na 1. Gdy użytkownik zaloguje się po raz pierwszy, sprawdzane jest credentials_expired, a zamiast zgłaszania wyjątku, zmienione hasło zostaje uruchomione. Dotarłem tak daleko.

ChangePasswordController będzie wtedy upewnić się, że hasło zostało faktycznie zmienione (to nie wydaje się być domyślnym zachowaniem w systemie FOS), a wartość credentials_expired jest ustawiona na 0. To jest, gdzie utknąłem. Jest tak wiele warstw usług, że nie mogę sprawić, że rzeczy będą odpowiednio dostosowane.

+0

Czy mógłbyś być bardziej konkretny? Istnieje wiele sposobów rozwiązania tego problemu. Opublikowanie kodu pomogłoby nam zidentyfikować sposób postępowania. W każdym razie, używanie ról częściej niż flag, może być dobrym pomysłem, ponieważ możesz nim zarządzać za pomocą zapory symfony. W związku z tym osoby z rolą CREDENTIAL_EXPIRED nie mogą uzyskać dostępu do całej sieci i utknęły w formularzu, który zmusza ich do zmiany hasła. – Manu

+0

Kod byłby FOSUserBundle. Rola jest świetnym pomysłem, ponieważ nie wymagałaby rozszerzania klas. Dam ci ten strzał. Dzięki. – David

Odpowiedz

12

Oto szczegółowa odpowiedź. Dzięki Manu za trampolinę!

Po pierwsze, upewnij się, aby uzyskać prawidłowy FOSUserBundle w pliku composer.json ("dev-master", a nie "*"):

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

Poniższy wszystko jest zawarte w moim pakiecie użytkownika, co rozszerza FOSUserBundle zgodnie z instrukcją instalacji.

PortalFlare/Paczka/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/Paczka/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/Paczka/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)); 
    } 
} 

Problem z FOSUserBundle, który nie zapewnia, że ​​użytkownik zmienia hasło, jest problemem na inny dzień.

Mam nadzieję, że to pomoże komuś.

4

Dobrym podejściem może być rozpoczęcie definiowania ROLE_USER, który będzie rolą, która ma dostęp do całej aplikacji. Gdy użytkownik zarejestruje się, automatycznie doda mu ROLE_CREDENTIAL_EXPIRED lub coś w tym stylu. Korzystając z JMSSecurityExtraBundle, możesz używać adnotacji w kontrolerze, decydując, czy użytkownicy z daną rolą mają do nich dostęp. Sprawdź również dokumentację dotyczącą tego, jak Symfony handle the HTTP Authentication.

+0

Zły @Manu zasługuje na awans Myślę, że :) –

+0

To bardzo miło z twojej strony! Dziękuję @ DarraghEnright :) – Manu

Powiązane problemy