2013-03-06 14 views
11

私は、管理者が最初のパスワードを持つユーザを作成し、ユーザが初めてログインしたときにパスワード変更ハンドラが自動的に起動するSymfony 2.1プロジェクトにセキュリティ機能を実装しようとしています。FOSUserBundleを使用してパスワードを強制的に変更する方法は?

私はFOSUserBundleクラスをオーバーライドする問題に遭遇しています。私はどこにいてもドキュメントには見えませんが、これはどういうわけか、少なくとも部分的にはすでに構築されていると確信しています。

エンティティでcredentials_expiredフラグを使用したいと思います。管理者がユーザーを作成すると、これは1に設定されます。ユーザーが最初にログインすると、credentials_expiredがチェックされ、例外がスローされるのではなく、change-passwordが実行されます。私はこれまでこれを作った。

ChangePasswordControllerは、パスワードが実際に変更されたことを確認します(これはFOSのデフォルト動作のようではありません)。また、credentials_expiredが0に設定されています。私は適切にカスタマイズされたものを得ることができないほど多くのサービス層があります。

+0

もっと具体的になりますか?これを解決するには多くのアプローチがあります。コードを投稿すると、どのように進行するのかを特定するのに役立ちます。とにかく、フラグよりも可能性の高い役割を使用することは、symfonyのファイアウォールで管理できるので、良い考えです。したがって、CREDENTIAL_EXPIREDロールを持つ人々はウェブ全体にアクセスすることができず、パスワードを変更するように強制されます。 – Manu

+0

コードはFOSUserBundleになります。その役割はクラスを拡張する必要がないため、素晴らしいアイデアです。私はそのショットを与えるでしょう。ありがとう。 – David

答えて

12

これは詳細な解答です。踏み台にマヌーに感謝します!

まず、composer.jsonファイル( "DEV-マスター"、NOT "*")で正しいFOSUserBundleを取得することを確認してください:

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

次は、すべて自分のユーザーバンドルに含まれています、インストール文書の指示どおりにFOSUserBundleを拡張します。

PortalFlare /バンドル/ UserBundle /リソース/設定/ 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 /バンドル/ UserBundle /イベントリスナー/ 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 /バンドル/ 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)); 
    } 
} 

FOSUserBundleの問題は、実際にユーザーがパスワードを変更したことを実際には確認していないという問題が別の日の問題です。

私はこれが誰かを助けてくれることを願っています。

4

良いアプローチは、アプリケーション全体にアクセスできる役割を果たすROLE_USERを定義することです。ユーザーが登録すると、ROLE_CREDENTIAL_EXPIREDなどが自動的に追加されます。 JMSSecurityExtraBundleを使用すると、コントローラーのアノテーションを使用して、特定の役割を持つユーザーがアクセスできるかどうかを判断できます。 Symfony handle the HTTP Authenticationのドキュメントも確認してください。

+0

悪い@Manuは私が考えるupvoteに値する:) –

+0

それはあなたの非常に親切です!ありがとう@ダーラーエイライト:) – Manu

関連する問題