2012-01-16 125 views
14

我們正在使用Symfony2的角色功能來限制用戶訪問我們應用的某些部分。我們的每個用戶實體都有許多具有開始日期和結束日期的訂閱實體,用戶可以購買年度訂閱。向用戶動態添加角色

現在,有沒有一種方法可以根據用戶是否擁有「主動」訂閱來向用戶動態添加角色?在rails中,我只是讓模型處理它是否具有必要的權限,但我知道symfony2實體不應該有權訪問Doctrine。

我知道,你可以從一個實體實例中訪問實體的關聯,但會經歷所有用戶的簽約對象,這似乎unnecessaryly麻煩我。

回答

27

我想你會做得更好,設置一個自定義選民和屬性。

/** 
* @Route("/whatever/") 
* @Template 
* @Secure("SUBSCRIPTION_X") 
*/ 
public function viewAction() 
{ 
    // etc... 
} 

SUBSCRIPTION_X角色(aka屬性)需要由自定義選民類來處理。

class SubscriptionVoter implements VoterInterface 
{ 
    private $em; 

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

    public function supportsAttribute($attribute) 
    { 
     return 0 === strpos($attribute, 'SUBSCRIPTION_'); 
    } 

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

    public function vote(TokenInterface $token, $object, array $attributes) 
    { 
     // run your query and return either... 
     // * VoterInterface::ACCESS_GRANTED 
     // * VoterInterface::ACCESS_ABSTAIN 
     // * VoterInterface::ACCESS_DENIED 
    } 
} 

您將需要配置和標記您的選民:

services: 
    subscription_voter: 
     class: SubscriptionVoter 
     public: false 
     arguments: [ @doctrine.orm.entity_manager ] 
     tags: 
      - { name: security.voter } 
+0

@ webda2l我不明白你的問題 – 2012-01-16 17:06:18

+0

對不起。我會盡量讓它更容易理解。 引發查詢的Voter類僅被用戶或每次頁面加載時調用一次? 在這最後一種情況下,爲了避免重複查詢,最好的方法是在投票功能中使用會話進行管理,不是嗎? – webda2l 2012-01-16 17:32:35

+0

您可以添加緩存機制或根據需要進行優化。 – 2012-01-16 17:34:28

2

假設您在用戶實體中擁有正確的關係「訂閱」。

你也許可以嘗試類似:

public function getRoles() 
{ 
    $todayDate = new DateTime(); 
    $activesSubscriptions = $this->subscriptions->filter(function($entity) use ($todayDate) { 
     return (($todayDate >= $entity->dateBegin()) && ($todayDate < $entity->dateEnd())); 
    }); 

    if (!isEmpty($activesSubscriptions)) { 
     return array('ROLE_OK'); 
    } 

    return array('ROLE_KO'); 
} 

角色轉變是可以做到的:

$sc = $this->get('security.context') 
$user = $sc->getToken()->getUser(); 
$user->setRole('ROLE_NEW'); 
// Assuming that "main" is your firewall name : 
$token = new \Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken($user, null, 'main', $user->getRoles()); 
$sc->setToken($token); 

但換頁後,供應商的refreshUser函數被調用,有時,作爲這是EntityUserProvider的情況,角色被查詢覆蓋。 您需要一個自定義提供程序來避免這種情況。

+0

我知道。但正如我所說的,我寧願不瀏覽所有訂閱對象,但讓MySQL執行「繁重的工作」,因爲這必須在每個請求中都發生。 (我從來沒有測試過這個,但是對我來說不必要的水合一堆物體似乎更慢,如果我錯了,請糾正我。) – maiwald 2012-01-16 13:46:29

+0

您可以使用form_login的success_handler選項來使用可訪問Doctrine的服務。 在它裏面,在onAuthenticationSuccess函數中,你要求獲得正確的角色。 而且,因爲每次頁面更改都會重新加載角色,所以您使用不刷新用戶的自定義用戶提供程序。 – webda2l 2012-01-16 16:17:00

+0

但是,如果角色在會話期間發生變化,用戶是否需要註銷並重新登錄?我不希望那樣。 – maiwald 2012-01-16 16:27:31