2012-07-02 16 views
8

मैं यह सत्यापित करने में सक्षम होना चाहता हूं कि Symfony2 में UserInterface लागू करने वाली किसी भी मनमानी वस्तु को गुण (भूमिकाएं) दी गई हैं या नहीं। क्या यह संभव है?मनमानी उपयोगकर्ताओं के प्रमाणीकरण के लिए Symfony2 में AccessDecisionManager का उपयोग कैसे करें?

UserInterface->getRoles() मेरी ज़रूरतों के लिए उपयुक्त नहीं है क्योंकि यह भूमिका में पदानुक्रम नहीं लेता है, और मैं उस विभाग में पहिया को फिर से नहीं खोलूंगा, इसलिए मैं संभवतः एक्सेस निर्णय प्रबंधक का उपयोग करना चाहता हूं ।

धन्यवाद।

आप isGranted विधि के साथ security.context सेवा का उपयोग कर सकते हैं:

नीचे ओलिवियर के समाधान के जवाब में, यहां मेरा अनुभव है। आप एक दूसरी तर्क पारित कर सकते हैं जो आपकी वस्तु है।

$user = new Core\Model\User(); 
var_dump($user->getRoles(), $this->get('security.context')->isGranted('ROLE_ADMIN', $user)); 

आउटपुट:

array (size=1) 
    0 => string 'ROLE_USER' (length=9) 

boolean true 

मेरी भूमिका पदानुक्रम:

role_hierarchy: 
    ROLE_USER:   ~ 
    ROLE_VERIFIED_USER: [ROLE_USER] 
    ROLE_ADMIN:   [ROLE_VERIFIED_USER] 
    ROLE_SUPERADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH] 
    ROLE_ALLOWED_TO_SWITCH: ~ 

मेरे UserInterface->getRoles() विधि:

public function getRoles() 
{ 
    $roles = [$this->isVerified() ? 'ROLE_VERIFIED_USER' : 'ROLE_USER']; 

    /** 
    * @var UserSecurityRole $userSecurityRole 
    */ 
    foreach ($this->getUserSecurityRoles() as $userSecurityRole) { 
     $roles[] = $userSecurityRole->getRole(); 
    } 

    return $roles; 
} 

ROLE_ADMIN स्पष्ट रूप से सौंपा जाना चाहिए, फिर भी isGranted('ROLE_ADMIN', $user) रिटर्न TRUE उपयोगकर्ता सिर्फ बनाया गया था और डिफ़ॉल्ट ROLE_USER, जब तक वर्तमान में उपयोगकर्ता के लॉग इन के अलावा अन्य किसी भी भूमिकाएं असाइन नहीं किया गया है, भले ही ROLE_ADMIN दी जाती है। इससे मेरा मानना ​​है कि दूसरे तर्क को isGranted() पर नजरअंदाज कर दिया गया है और TokenAccessDecisionManager->decide() को SecurityContext द्वारा प्रदान किया जाता है।

यदि यह एक बग है तो मैं एक रिपोर्ट सबमिट करूंगा, लेकिन शायद मैं अभी भी कुछ गलत कर रहा हूं?

उत्तर

1

RoleVoter $ वस्तु SecurityContext->isGranted() से के माध्यम से पारित की उपेक्षा। इसके परिणामस्वरूप RoleHierarchyVoterToken से UserInterface $ ऑब्जेक्ट (यदि मौजूद है) के बजाय भूमिकाओं को निकालने के परिणामस्वरूप, तो मुझे एक अलग मार्ग खोजना पड़ा।

हो सकता है कि वहाँ इस बारे में जाने के लिए एक बेहतर तरीका है और अगर वहाँ मुझे यकीन है कि पता करने के लिए चाहते हैं, लेकिन इस समाधान मैं के साथ आया है:

सबसे पहले मैं अपने उपयोगकर्ता कक्षा में ContainerAwareInterface लागू किया तो मैं यह भीतर से सुरक्षा घटक पहुंच सकता है:

final class User implements AdvancedUserInterface, ContainerAwareInterface 
{ 
    // ... 

    /** 
    * @var ContainerInterface 
    */ 
    private $container; 

    // ... 

    public function setContainer(ContainerInterface $container = null) 
    { 
     if (null === $container) { 
      throw new \Exception('First argument to User->setContainer() must be an instance of ContainerInterface'); 
     } 

     $this->container = $container; 
    } 

    // ... 
} 

तब मैं परिभाषित एक hasRole() विधि:

/** 
* @param string|\Symfony\Component\Security\Core\Role\RoleInterface $roleToCheck 
* @return bool 
* @throws \InvalidArgumentException 
*/ 
public function hasRole($roleToCheck) 
{ 
    if (!is_string($roleToCheck)) { 
     if (!($roleToCheck instanceof \Symfony\Component\Security\Core\Role\RoleInterface)) { 
      throw new \InvalidArgumentException('First argument expects a string or instance of RoleInterface'); 
     } 
     $roleToCheck = $roleToCheck->getRole(); 
    } 

    /** 
    * @var \Symfony\Component\Security\Core\SecurityContext $thisSecurityContext 
    */ 
    $thisSecurityContext = $this->container->get('security.context'); 
    $clientUser = $thisSecurityContext->getToken()->getUser(); 

    // determine if we're checking a role on the currently authenticated client user 
    if ($this->equals($clientUser)) { 
     // we are, so use the AccessDecisionManager and voter system instead 
     return $thisSecurityContext->isGranted($roleToCheck); 
    } 

    /** 
    * @var \Symfony\Component\Security\Core\Role\RoleHierarchy $thisRoleHierarchy 
    */ 
    $thisRoleHierarchy = $this->container->get('security.role_hierarchy'); 
    $grantedRoles = $thisRoleHierarchy->getReachableRoles($this->getRoles()); 

    foreach ($grantedRoles as $grantedRole) { 
     if ($roleToCheck === $grantedRole->getRole()) { 
      return TRUE; 
     } 
    } 

    return FALSE; 
} 

एक नियंत्रक से:

$user = new User(); 
$user->setContainer($this->container); 

var_dump($user->hasRole('ROLE_ADMIN')); 
var_dump($this->get('security.context')->isGranted('ROLE_ADMIN')); 
var_dump($this->get('security.context')->isGranted('ROLE_ADMIN', $user)); 

$user->addUserSecurityRole('ROLE_ADMIN'); 
var_dump($user->hasRole('ROLE_ADMIN')); 

आउटपुट:

boolean false 
boolean true 
boolean true 

boolean true 

हालांकि यह AccessDecisionManager या पंजीकृत मतदाताओं को शामिल नहीं करता है (जब तक उदाहरण परीक्षण किया जा रहा है वर्तमान में प्रमाणित उपयोगकर्ता), यह मेरी जरूरतों के लिए पर्याप्त है के रूप में मैं सिर्फ करने की जरूरत है यह सुनिश्चित करें कि किसी दिए गए उपयोगकर्ता की कोई विशेष भूमिका है या नहीं।

+0

सिमफोनी 2.3 –

-3

आप सेवा isGranted विधि के साथ उपयोग कर सकते हैं।

आप अपना दूसरा ऑब्जेक्ट पास कर सकते हैं (here देखें)।

एक नियंत्रक में:

$this->get('security.context')->isGranted('ROLE_FOOBAR', $myUser) 
+0

धन्यवाद, लेकिन यह पहली तरीकों मैं प्रयास किया में से एक था और यह काम करने के लिए प्रकट नहीं होता। यह अभी भी मौजूदा उपयोगकर्ता के टोकन के आधार पर निर्णय लेता है जो मैं बता सकता हूं। मैंने अपनी मूल पोस्ट संपादित की है और मेरे सेटअप पर आपके सुझाव का परिणाम प्रदान किया है। –

+1

'SecurityContext-> निष्पादन()' के निष्पादन का पता लगाने के बाद, ऐसा लगता है कि मतदान प्रक्रिया में '$ ऑब्जेक्ट 'कभी नहीं माना जाता है। 'रोलवॉटर-> वोट()' तर्क के रूप में '$ ऑब्जेक्ट' स्वीकार करता है, लेकिन चर का उपयोग विधि निकाय में नहीं किया जाता है और भूमिकाओं को '$ टोकन' तर्क से निकाला जाता है ('AccessDecisionManager-> से पारित किया जाता है) तय करें() ''GGanted()' में कॉल कॉल, 'सुरक्षा कॉन्टेक्स्ट' की 'टोकन' प्रॉपर्टी पर सेट होने के साथ)। –

+4

यह * वर्तमान में प्रमाणीकृत उपयोगकर्ता * के लिए अनुमतियों की जांच करेगा और $ myUser के लिए नहीं। –

0

इस के साथ एक समस्या की तरह दिखता है: निर्माता पर

abstract class AbstractToken implements TokenInterface

देखो। ऐसा लगता है कि तत्कालता पर भूमिकाएं बनाई जाती हैं और रन टाइम पर पूछताछ नहीं की जाती हैं।

public function __construct(array $roles = array()) 
{ 
    $this->authenticated = false; 
    $this->attributes = array(); 

    $this->roles = array(); 
    foreach ($roles as $role) { 
     if (is_string($role)) { 
      $role = new Role($role); 
     } elseif (!$role instanceof RoleInterface) { 
      throw new \InvalidArgumentException(sprintf('$roles must be an array of strings, or RoleInterface instances, but got %s.', gettype($role))); 
     } 

     $this->roles[] = $role; 
    } 
} 

इसलिए, टोकन बनने के बाद भूमिकाएं बदल नहीं सकती हैं। मुझे लगता है कि विकल्प अपना मतदाता लिखना है। मैं अभी भी चारों ओर देख रहा हूँ।

+0

में '$ givenRole-> getRole()' के भीतर त्रुटि प्राप्त करना यह अभी भी यह नहीं बताता है कि क्यों सिम्फनी भूमिका पदानुक्रम और दूसरी सुरक्षा 'सुरक्षा कॉन्टेक्स्ट-> isGranted' को मानता है। ऐसा लगता है कि कार्यान्वयन संभवतः अपूर्ण है? किसी भी तरह से मुझे नहीं लगता कि समाधान एक और मतदाता पंजीकृत कर रहा है। मैंने बाद में अपने पोस्ट समाधान को थोड़ा सा संशोधित किया है और इसे मॉडल से एक सेवा में स्थानांतरित कर दिया है, और यह अच्छी तरह से काम कर रहा है। मैं जल्द ही अद्यतन समाधान पोस्ट करूंगा। –

+0

भूमिका पदानुक्रम 'RoleHierarchyVoter' द्वारा ध्यान में रखा जाता है, यह सिम्फनी सुरक्षा घटक –

3

हो सकता है कि आप एक नया securityContext उदाहरण का दृष्टांत और इसका इस्तेमाल की जांच करने के लिए यदि उपयोगकर्ता प्रदान किया जाता है कर सकते हैं:

$securityContext = new \Symfony\Component\Security\Core\SecurityContext($this->get('security.authentication.manager'), $this->get('security.access.decision_manager')); 
$token   = new \Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken($user, null, $this->container->getParameter('fos_user.firewall_name'), $user->getRoles()); 
$securityContext->setToken($token); 
if ($securityContext->isGranted('ROLE_ADMIN')) { 
    // some stuff to do 
} 
0

एक सेवा के रूप में एक सेवा AccessDecisionMaker (प्रयुक्त छायादार के समाधान) बनाएं

<?php 
namespace Bp\CommonBundle\Service; 

use Symfony\Component\DependencyInjection\Container; 
use Symfony\Component\Security\Core\Role\RoleInterface; 
use Symfony\Component\Security\Core\User\UserInterface; 
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; 
use Symfony\Component\Security\Core\SecurityContext; 

class AccessDecisionMaker 
{ 
    /** @var Container */ 
    private $container; 

    /** @var SecurityContext */ 
    private $securityContext; 

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

     if (!$this->securityContext) { 
      // Ensure security context is created only once 
      $this->securityContext = new SecurityContext($this->container->get(
       'security.authentication.manager' 
      ), $this->container->get('security.access.decision_manager')); 
     } 
    } 

    public function isGranted($roleToCheck, UserInterface $user) 
    { 
     if (!is_string($roleToCheck)) { 
      if (!($roleToCheck instanceof RoleInterface)) { 
       throw new \InvalidArgumentException('First argument expects a string or instance of RoleInterface'); 
      } 
      $roleToCheck = $roleToCheck->getRole(); 
     } 

     $token = new UsernamePasswordToken($user, null, $this->container->getParameter(
      'fos_user.firewall_name' 
     ), $user->getRoles()); 
     $this->securityContext->setToken($token); 
     if ($this->securityContext->isGranted($roleToCheck)) { 
      return true; 
     } 

     return false; 
    } 

} 

कॉन्फ़िगर इस

bp.access_decision_maker: 
    class: Bp\CommonBundle\Service\AccessDecisionMaker 
    arguments: [@service_container ] 

इसका उपयोग करें

$this->container->get('bp.access_decision_maker')->isGranted("ROLE_ADMIN",$user); 
13

आप केवल AccessDecisionManager इस बात के लिए की जरूरत है, सुरक्षा संदर्भ की कोई आवश्यकता नहीं है क्योंकि आप प्रमाणीकरण की जरूरत नहीं है।

$user = new Core\Model\User(); 

$token = new UsernamePasswordToken($user, 'none', 'none', $user->getRoles()); 
$isGranted = $this->get('security.access.decision_manager') 
    ->decide($token, array('ROLE_ADMIN')); 

यह सही ढंग से ध्यान में भूमिका पदानुक्रम ले जाएगा के बाद से RoleHierarchyVoter डिफ़ॉल्ट रूप से पंजीकृत है

अद्यतन

@redalaanait द्वारा बताया गया है, security.access.decision_manager एक निजी सेवा है , इसलिए इसे सीधे एक्सेस करना एक अच्छी बात नहीं है। service aliasing का उपयोग करना बेहतर है, जो आपको निजी सेवाओं तक पहुंचने की अनुमति देता है।

+2

में बनाया गया है यह सबसे सुरुचिपूर्ण समाधान है। धन्यवाद –

+1

मुझे लगता है कि हम कंटेनर से सीधे 'security.access.decision_manager' नहीं प्राप्त कर सकते क्योंकि यह एक निजी सेवा है ?? –

+1

@redalaanait आप सही हैं, एक निजी सेवा का उपयोग करना एक अच्छी बात नहीं है। लेकिन आप [सेवा एलियासिंग] (http://symfony.com/doc/current/components/dependency_injection/advanced.html#aliasing) का उपयोग कर सकते हैं, जो आपको निजी सेवाओं तक पहुंचने की अनुमति देता है। –

2

security.context 2.6 के बाद से बहिष्कृत किया गया है।

उपयोग AuthorizationChecker:

$token = new UsernamePasswordToken(
    $user, 
    null, 
    'secured_area', 
    $user->getRoles() 
); 
$tokenStorage = $this->container->get('security.token_storage'); 
$tokenStorage->setToken($token); 
$authorizationChecker = new AuthorizationChecker(
    $tokenStorage, 
    $this->container->get('security.authentication.manager'), 
    $this->container->get('security.access.decision_manager') 
); 
if (!$authorizationChecker->isGranted('ROLE_ADMIN')) { 
    throw new AccessDeniedException(); 
}