Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Еще раз о Security в Symfony2 подход user-resource-privilege

Anna | 31.05.2014 | нет комментариев

Не так давным-давно взялся за Symfony2. Не смотря на то, что до этого имел довольно богатенький навык общения с Zend1, барьер входа для меня оказался высоким. Вдоволь начитавшись у меня предисловие что-то получаться. Наибольшие затруднения вызвал вопрос разграничения прав доступа. Фактически все мои искания выводили меня на FOSUserBundle либо обрывки информации о том, как дозволено расширить функционал модуля Security из стандартной поставки фреймворка. Каких-либо превосходств для себя в массивном FOSUserBundle я не нашел. Следственно эта статья будет о том, как я допиливал Symfony2 Security под свои нужды. Цель была дальнейшая: symfony2 security разграничение прав доступа на ярусе объекта в зависимости от роли пользователя. В этой статье не будет ничего про наследование ролей и кумулятивные привилегии, информацию о которых вы, без труда, обнаружите сами. Схема прав в моем плане: запрещено все, что не разрешено. Один пользователь имеет сурово одну роль. Роль имеет доступ к разным источникам с разным комплектом привилегий. Различные роли могут иметь доступ к одним и тем же источникам с различными либо равными комплектами привилегий. Я не буду пытаться сделать код максимально абстрактным, а легко буду применять фрагменты из своего плана, связанные с функциональностью заказ-нарядов на обслуживание техники.

Выходит, к делу. У нас есть верно настроенный план, в нем сделан BackendWorkorderBundle, настроены все роутеры и фаерволы. Т.е. есть все, за исключением прав доступа. Включая аутификацию. Для проектирование БД применялся инструмент MySQL Workbench. Хорошая штука. Есть версия под Linux. Конструкция таблиц выглядит так:

-- -----------------------------------------------------
-- Table `backend_role`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `backend_role` (
  `role_id` INT NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(45) NULL,
  `description` VARCHAR(45) NULL,
  PRIMARY KEY (`role_id`))
ENGINE = InnoDB;

-- -----------------------------------------------------
-- Table `backend_user`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `backend_user` (
  `user_id` INT NOT NULL AUTO_INCREMENT,
  `role_id` INT NOT NULL,
  `firstname` VARCHAR(45) NULL,
  `lastname` VARCHAR(45) NULL,
  `printname` VARCHAR(45) NULL,
  `username` VARCHAR(45) NULL,
  `salt` VARCHAR(255) NULL,
  `password` VARCHAR(255) NULL,
  `created` DATETIME NULL,
  `updated` DATETIME NULL,
  `last_login` DATETIME NULL,
  `is_active` TINYINT(1) NULL,
  PRIMARY KEY (`user_id`),
  INDEX `fk_backend_user_backend_role1_idx` (`role_id` ASC),
  CONSTRAINT `fk_backend_user_backend_role1`
    FOREIGN KEY (`role_id`)
    REFERENCES `parts`.`backend_role` (`role_id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;

-- -----------------------------------------------------
-- Table `backend_rule`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `backend_rule` (
  `rule_id` INT NOT NULL AUTO_INCREMENT,
  `role_id` INT NOT NULL,
  `resource_id` VARCHAR(255) NULL,
  `privileges` TEXT NULL,
  PRIMARY KEY (`rule_id`),
  INDEX `fk_backend_rule_backend_role1_idx` (`role_id` ASC),
  CONSTRAINT `fk_backend_rule_backend_role1`
    FOREIGN KEY (`role_id`)
    REFERENCES `parts`.`backend_role` (`role_id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;

Проверять присутствие привилегий дозволено двумя методами:
1. Из twig is_granted('[наименование привилегии]', [объект])
2. Из контроллера $this->get('security.context')->isGranted('[наименование привилегии]', [объект])
2-й довод не непременен, но нужен для целей моего плана (станет ясно чуть ниже в коде voter’а). Напоминаю, что исключение объекта из html страницы не отменяет проверку данных в контроллере.

Код voter’a. Позабыл упомянуть, что в плане есть есть еще один бандл BackendCoreBundle, которые вбирает в себя особенно всеобщие функции для каждого Backend’a. Подробнее о voter’ах дозволено почитать тут.

<?php
// /src/Backend/CoreBundle/Security/Authorization/Voter/PrivilegeVoter.php

namespace Backend\CoreBundle\Security\Authorization\Voter;

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

class PrivilegeVoter implements VoterInterface 
{
    public function supportsAttribute($attribute) 
    {
        return true;
    }

    public function supportsClass($class)
    {
        return in_array($class, array(
          'Backend\WorkorderBundle\Entity\Workorder'
       ));
    }

    public function vote(TokenInterface $token, $object, array $attributes)
    {
	//применим ли voter к объекту определенного класса.
	//необходимо так как наш вотер будет опрашиваться во всех случаях контроля привилегий.
        if ( !($this->supportsClass(get_class($object))) ) {
            return VoterInterface::ACCESS_ABSTAIN;
        }

        foreach ($attributes as $attribute) { //необходимо адаптировать функцию под ваши нужды
            if ( !$this->supportsAttribute($attribute) ) {
                return VoterInterface::ACCESS_ABSTAIN;
            }
        }

	//магия творится тут
        $user = $token->getUser();
        $privileges = $user->getPrivileges();
        $resourceId = $object->getResourceId();

        $acess_granted = false;
        foreach ($attributes as $attribute) {
            if (isset($privileges[$resourceId])) {
                $resource_privileges = $privileges[$resourceId];
                if (in_array($attribute, $resource_privileges)) {
                    $acess_granted = true;
                } else {
                    $acess_granted = false;
                    break;
                }
            }
        }

        if ($acess_granted)
            return VoterInterface::ACCESS_GRANTED;

       return VoterInterface::ACCESS_DENIED;
    }
}

Фунция getPrivileges для user объявлена в объекте doctrine, связанном с таблицей backend_user

<?php
///src/Backend/CoreBundle/Entity/BackendUser.php

namespace Backend\CoreBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;

/**
 * BackendUser
 *
 * @ORM\Table(name="backend_user")
 * @ORM\Entity
 */
class BackendUser implements AdvancedUserInterface, \Serializable
{
..
    public function getPrivileges()
    {
	//цепочка выглядит так: backend_user->backend_role->backend_rule
	//функция $rule->getPrivileges() возвращает значение поля privileges таблицы backend_rule
	//то есть нынешняя функция возвращает массив ключами которого являеются resource_id,
	//а элементами массивы привилений для доступа к этому источнику (хранятся через запятую)

        $rules = $this->getRole()->getRules();
        $result = array();
        foreach ($rules as $rule){
            $result[$rule->getResourceId()] = explode(",", $rule->getPrivileges());
        }
        return $result;
    }
..
}

Регистрируем voter в /app/config/security.yml

services:
    security.access.privilege_voter:
        class:      Backend\CoreBundle\Security\Authorization\Voter\PrivilegeVoter
        public:     false
        tags:
           - { name: security.voter }

Вы, вероятно, обратили внимание, что в функции vote вызывается $object->getResourceId(). Выглядит способ дальнейшим образом

<?php
// /src/Backend/WorkorderBundle/Entity/Workorder.php
namespace Backend\WorkorderBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;

use Doctrine\ORM\Mapping as ORM;

/**
 * Workorder
 *
 * @ORM\Table(name="workorder")
 * @ORM\Entity
 */
class Workorder
{
..
    public function getResourceId()
    {
	//функция добавлена для эластичности и в нынешний момент возвращает имя класса
	//В данном случае Backend\WorkorderBundle\Entity\Workorder
        return get_class($this);
    }
..
}

That’s it! Критика, как традиционно, привествуется, если кто-то может указать на недостатвки этого подхода и допустимые задачи при масштабировании — был бы дюже рад.

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB