2011-10-24 42 views
26

我想知道是否有人知道使用Symfony2 ACL系統實現此目的的一種優雅方法。Symfony2 ACL與另一個條件相結合

我有一個Comment實體(我的域對象),這需要通過ROLE_USER可編輯,但這只是5分鐘的註釋中不允許張貼 - 否則的評論只有ROLE_ADMIN進行編輯。

使其只能編輯ROLE_USERROLE_ADMIN很簡單,只需爲每個設置一個RoleSecurityIdentity即可。

現在我的問題發生在我想要合併ROLE_USER的時間因素。我的第一個問題是,它需要來自域對象的信息,而不僅僅是ACL表,但我認爲這可以通過創建一個自定義ObjectIdentity類來解決,該類也可以保存Comment的發佈時間。

現在最困難的部分

我想我需要創建一個自定義PermissionGrantingStrategy,知道也看的創建時間。必須在檢查Comment類型時加載,但我不知道如何加載它。有誰知道是否有某種工廠可以通過這種工具進行配置?所以如果一個實體有一個特定的PermissionGrantingStrategy與它關聯,那麼它會被使用,否則使用默認值?

我知道這有點長,很多感謝如果有人知道如何做到這一點,因爲ACL文檔目前似乎稀少。我的回退解決方案是簡單地提供某種服務來檢查是否可以編輯評論,而不用打擾ACL。

回答

23

你認爲有用選民?有一個cookbook recipe實現IP黑名單選民,但它可以很容易地修改,以處理檢查評論對象的編輯。

你可以在Symfony\Component\Security\Acl\Voter\AclVoter(在線here)查看默認的AclVoter,雖然你的顯然可以增加而不是替換它,並且更簡單。

作爲概念證明快速:

class CommentTimestampVoter implements VoterInterface 
{ 
    public function supportsAttribute($attribute) 
    { 
     return 'edit' === $attribute; 
    } 

    public function vote(TokenInterface $token, $object, array $attributes) 
    { 
     // 1. check if $token->getUser() has ROLE_ADMIN and return VoterInterface::ACCESS_GRANTED if so 
     // 2. check if $token->getUser() equals $object->getAuthor() and return VoterInterface::ACCESS_DENIED if not 
     // 3. check that $object->getCreatedAt() is within the window allowed for editing and return VoterInterface::ACCESS_GRANTED if so 
     // 4. return VoterInterface::ACCESS_DENIED 
    } 

    public function supportsClass($class) 
    { 
     return 'Acme\CommentBundle\Entity\Comment' === $class; 
    } 
} 
+0

這聽起來像我所需要的,插入很好,也可以通過安全上下文的isGranted(它也可以在我的前端用於有條件地顯示控件以編輯註釋)訪問。今晚我會給它一個鏡頭,如果它有效,請勾選答案,但看起來你已成爲贏家:)謝謝! – Kasheen

+0

確定實施此解決方案後,我會說這是要走的路,但我會在下面發佈我的代碼,因爲它會幫助任何人,因爲我在創建投票人時發現了很多陷阱,因此希望我的代碼可以充當文檔(儘管我不能保證正確)給其他人。 – Kasheen

36

我張貼這種解決方案,以便其他人可以看到我的最終代碼,但在這裏實行的是選民的時候有問題,建議我發現的陷阱。

supportsAttribute:看來,當你調用isGranted方法上SecurityContext,它實際上並沒有委託你vote方法內vote呼叫到VoterInterface所以之前檢查這個方法你確實有自己檢查屬性。

supportsClass:在它上面的問題的答案似乎是這種方法可能是一個工廠爲基礎的選擇,其中VoterInterface S可投票的關鍵,但實際上Symfony2的文件上寫着:

的supportsClass()方法用於檢查選舉器是否支持當前用戶令牌類。

因此,它實際上似乎與Voter是否支持令牌類型有關。更糟糕的是,PHP Doc似乎很模糊:

檢查選舉器是否支持給定的類。

在任何情況下的主要問題是,這種方法從未被SecurityContext電話委託給任何選民的vote方法前檢查 - 即使這種方法是硬編碼到return falsevote仍將被稱爲!

所以基本上這個故事的寓意似乎是:檢查$attributes$object手動進來的vote方法。

我的代碼:

services.yml

parameters: 
    comment_voter.class: Acme\Bundle\CommentBundle\Security\Authorization\Voter\CommentVoter 

services: 
    comment_voter: 
     class: %comment_voter.class% 
     arguments: [@service_container] 
     public: false 
     tags: 
      - { name: security.voter } 

和選民類:

<?php 

namespace Acme\Bundle\CommentBundle\Security\Authorization\Voter; 

use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; 
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; 

use Acme\Bundle\CommentBundle\Entity\Comment; 
use Symfony\Component\Security\Core\User\UserInterface; 

/** 
* A class to check editing privileges for Comments. 
*/ 
class CommentVoter implements VoterInterface { 

    const AUTHOR_EDIT_TIME_LIMIT = 300; 

    private $container; 

    public function __construct($container) { 
     $this->container = $container; 
    } 

    public function supportsAttribute($attribute) { 
     return $attribute === 'EDIT'; 
    } 

    public function supportsClass($class) { 
     return true; 
    } 

    /** 
    * Checks whether or not the current user can edit a comment. 
    * 
    * Users with the role ROLE_COMMENT_MODERATOR may always edit. 
    * A comment's author can only edit within 5 minutes of it being posted. 
    * 
    * {@inheritdoc} 
    */ 
    public function vote(TokenInterface $token, $object, array $attributes) { 
     if (!($object instanceof Comment)) { 
      return VoterInterface::ACCESS_ABSTAIN; 
     } 

     // Only supports 'EDIT' for now. 
     if (!$this->supportsAttribute($attributes[0])) { 
      return VoterInterface::ACCESS_ABSTAIN; 
     } 

     $user = $token->getUser(); 
     if (!($user instanceof UserInterface)) { 
      return VoterInterface::ACCESS_DENIED; 
     } 

     // Is the token a comment moderator? 
     if ($this->container->get('security.context')->isGranted('ROLE_COMMENT_MODERATOR')) { 
      return VoterInterface::ACCESS_GRANTED; 
     } 

     // Is the token the author of the post and within the edit window. 
     $originalRevision = $object->getOriginalRevision(); 
     if ($originalRevision->getAuthor()->equals($user)) { 
      if ( 
       (time() - $originalRevision->getCreationDate()->getTimestamp()) 
       <= self::AUTHOR_EDIT_TIME_LIMIT 
      ) { 
       return VoterInterface::ACCESS_GRANTED; 
      } 
     } 

     return VoterInterface::ACCESS_DENIED; 
    } 

} 

終於模板:

{% if is_granted('EDIT', comment) %}<a href="#">Edit</a>{% endif %} 

我希望這可以幫助未來的其他人,並且非常感謝有問題的指向我的選民的方向。

+6

_偉大除了這裏。感謝您的拼搏! – Problematic

+6

謝謝@Problematic和kasheen,這將是Symfony烹飪書https://github.com/symfony/symfony-docs的一個很好的補充;)你應該提交一個PR。 – maxwell2022

相關問題