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! }