symfonyで独自のpermissionチェックを実装する

メニューをデータベースで管理して、それをそのまま権限チェックに使用したかったので調べてみた。

How to Use Voters to Check User Permissions で解説されている、Voterクラスを定義してサービス追加することで可能。
注意としては バージョンにより頻繁に実装するメソッド名が変わる ことである。

ログイン時にVoterで使うための権限をセットしたい場合は security.interactive_login でイベントフックする。詳しくは Authentication で解説されている。

使用例

GroupにMenuListをリンクさせて、MenuListのpathを権限として扱う。SonataUserBundleを使用。現在作業してる環境の都合上version2.3をベースにしてある。

ログイン時のイベント

 
class LoginListener
{
	/**
	 *
	 * @param InteractiveLoginEvent $event
	 */
	public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
	{
		$user = $event->getAuthenticationToken()->getUser();
		/* @var $user   \Application\Sonata\UserBundle\Entity\User */
 
		$groups = $user->getGroups();
		$menulists = array();
		foreach($groups as $group)
		{
			/* @var $group   \Application\Sonata\UserBundle\Entity\Group */
			foreach($group->getMenuLists() as $menulist)
			{
				$menulists[] = $menulist->getPath();
			}
		}
		$event->getAuthenticationToken()->setAttribute("__menu_roles", $menulists);
	}
}
 

services.ymlに追加

 
#Application\Sonata\UserBundle\Resources\config\services.yml
    security.menulogin.listener:
        class: Application\Sonata\UserBundle\Listener\LoginListener
        tags:
            - { name: kernel.event_listener, event: security.interactive_login, method: onSecurityInteractiveLogin}
 

ここまでで、ログイン時に__menu_rolesセッション内にmenu配列が登録される。

Voterクラスを作成

 
class MenuVoter implements VoterInterface
{
 
	/*
         * 本来はここでisGrantedで指定した値が、メニューリスト内に存在する値かチェックする。
	 * @see \Symfony\Component\Security\Core\Authorization\Voter\VoterInterface::supportsAttribute()
	 */
	public function supportsAttribute($attribute) {
		return true;//面倒なのでattributeのチェックはしない 
	}
 
	public function supportsClass($class) {
		return true;
	}
 
 
	/*
	 * @see \Symfony\Component\Security\Core\Authorization\Voter\VoterInterface::vote()
	 */
	public function vote(TokenInterface $token, $object, array $attributes) {
 
		foreach($token->getAttribute("__menu_roles") as $role)
		{
			if(in_array($role, $attributes))
			{
				return true;
			}
		}
		return false;
	}
}
 

services.ymlに Voterイベントを追加

 
    security.access.menu_voter:
        class:      Application\Sonata\UserBundle\Security\MenuVoter
        public:     false
        tags:
            - { name: security.voter }
 

コントローラ内で使用

 
if($this->get('security.context')->isGranted("path/info"))
{
  //granted!
}