ФорумПрограммированиеPHP для идиотов → Эксперименты с RBAC

Эксперименты с RBAC

  • artoodetoo

    Сообщения: 5147 Репутация: N Группа: в ухо

    Spritz 9 сентября 2009 г. 6:11, спустя 8 часов 7 минут 12 секунд

    Захотелось мне сделать свой контроль доступа на основе ролей на PHP. Кто в теме — поймет. (edited: Оказалось нет таких. Вот здесь я надергал цитат про RBAC. Почитайте, затем смотрите пример)

    Хотелось бы услышать толковую критику и, может быть, примеры как эффективно реализовать RBAC на SQL.

    <?php
    /********************************************************************
    * - tasks can have sub-tasks and so on. depth is unlimited
    * - task unfolding pre-process
    * - deny-allow order is hardcoded (always deny first)
    ********************************************************************/


    // Dummy meta-data model
    class CPermsModel
    {
    private function _unfoldTask($entity, &$tasks)
    {
    $result = array($entity);
    foreach ($tasks[$entity] as $e)
    if (isset($tasks[$e]))
    $result += $this->unfoldTask($e);
    else
    $result[] = $e;
    return $result;
    }

    public function getRoles()
    {
    return array('administrator', 'moderator', 'user', 'guest', 'post owner', 'banned');
    }

    public function getTasks()
    {
    return array(
    'user management' => array('create user', 'modify user', 'delete user'),
    'post management' => array('create post', 'modify post', 'delete post')
    );
    }

    public function getGrants()
    {
    return array(
    array('administrator', 'allow', 'user management'),
    array('administrator', 'allow', 'post management'),
    array('moderator', 'allow', 'post management'),
    array('banned', 'deny', 'user management'),
    array('banned', 'deny', 'post management'),
    // array('user', 'allow', 'post management'),
    array('user', 'allow', 'create post'),
    array('post owner', 'allow', 'modify post'),
    array('post owner', 'allow', 'delete post'),
    );
    }

    public function getUnfoldedGrants()
    {
    $tasks = $this->getTasks();
    $tmp = $this->getGrants();

    // Reform grants as grants[role][entity] = permission
    $grants = array();
    foreach ($tmp as $g)
    {
    list($role, $perm, $entity) = $g;
    if (isset($tasks[$entity]))
    {
    // Unfold tasks in grant list
    foreach ($this->_unfoldTask($entity, $tasks) as $e)
    $grants[$role][$e] = $perm;
    }
    else
    $grants[$role][$entity] = $perm;
    }

    return $grants;
    }
    }


    // Simple permission checker
    class CPermsChecker
    {
    /* — private section – */

    private $roles;
    private $grants;

    //
    // Check one role for specified action or task
    // returns allow|deny|NULL
    private function findGrant($role, $entity)
    {
    if (isset($this->grants[$role][$entity]))
    return $this->grants[$role][$entity];
    return NULL;
    }

    /* — public section – */

    function __construct(CPermsModel $model)
    {
    // Fetch meta-data from database
    $this->roles = $model->getRoles();
    $this->grants = $model->getUnfoldedGrants();
    }

    //
    // Can it do the action?
    // returns allow|deny
    // first arg is object having roles or roles themleslf
    // second arg is action or task
    //
    public function can($object, $entity)
    {
    // It is an object_with_roles_property OR array_of_role_ids OR role_id
    if (is_object($object))
    $roles = $object->roles;
    else if (is_array($object))
    $roles = $object;
    else
    $roles = array($object);

    $result = NULL;

    // Test every role…
    foreach ($roles as $role)
    {
    $g = $this->findGrant($role, $entity);
    // … until first deny rule
    if (!is_null($g))
    {
    $result = $g;
    if ($g == 'deny')
    break;
    }
    }

    if (is_null($result))
    return 'deny';

    return $result;
    }

    //
    // Get role_ids array who can|cannot do the action
    // first arg is action or task
    // second arg is deny|allow
    //
    public function whoCan($entity, $value = 'allow')
    {
    $result = array();

    foreach ($this->roles as $role)
    {
    $g = $this->findGrant($role, $entity);
    if ($g == $value || (is_null($g) && $value == 'deny'))
    $result[] = $role;
    }

    return $result;
    }
    }


    // Tests:

    $model = new CPermsModel;
    $p = new CPermsChecker($model);

    header('Content-type: text/plain');

    echo "By default = deny";

    echo "\n\n   role - operation";
    echo "\n1. ".$p->can('guest', 'delete post');
    echo "\n2. ".$p->can('user', 'delete post');
    echo "\n3. ".$p->can('post owner', 'delete post');
    echo "\n4. ".$p->can('moderator', 'delete post');

    echo "\n\n   role - task";
    echo "\n1. ".$p->can('moderator', 'post management');
    echo "\n2. ".$p->can('moderator', 'user management');

    echo "\n\n   role set - operation";
    echo "\n1. ".$p->can(array('user', 'post owner'), 'delete post');
    echo "\n2. ".$p->can(array('user', 'banned'), 'delete post');
    echo "\n3. ".$p->can(array('user', 'moderator', 'banned'), 'delete post');

    echo "\n\n   role set - task";
    echo "\n1. ".$p->can(array('user'), 'post management');
    echo "\n1. ".$p->can(array('user', 'moderator'), 'post management');
    echo "\n2. ".$p->can(array('user', 'moderator', 'banned'), 'post management');

    echo "\n\n   who can";
    echo "\ncreate post:     ".implode(', ', $p->whoCan('create post'));
    echo "\ndelete post:     ".implode(', ', $p->whoCan('delete post'));
    echo "\npost management: ".implode(', ', $p->whoCan('post management'));

    echo "\n\n   who cannot";
    echo "\ncreate post:     ".implode(', ', $p->whoCan('create post', 'deny'));
    echo "\ndelete post:     ".implode(', ', $p->whoCan('delete post', 'deny'));
    echo "\npost management: ".implode(', ', $p->whoCan('post management', 'deny'));


    edited: что делается

    В моем примере реализуется такая версия RBAC:
    - роли не могут быть вложены, но
    - задачи могут иметь подзадачи
    - что не разрешено, то запрещено
    - пользователю могут быть назначены несколько ролей одновременно. класс "пользователь" я не создавал, тестировать можно через массив ролей или через любой объект со свойством roles

    Метод can() проверяет доступно ли пользователю (набору ролей) какое-то действие или задача
    Метод whoCan() возвращает список ролей, которые дают/запрещают указанное действие или задачу

    1. RBAC.ZIP (46)
    ιιlllιlllι унц-унц
  • Timur

    Сообщения: 1068 Репутация: N Группа: Джедаи

    Spritz 8 сентября 2009 г. 20:03, спустя 13 часов 51 минуту 49 секунд


    array('moderator', 'allow', 'post management'),
    array('banned', 'deny', 'user management'),


    Зачем отдельно хранить записи о том что "разрешено" и "не разрешено"? Т. е. почему бы просто не указывать для каждой роли список разрешенных действий. Если действия нет в списке разрешенных, оно считается "deny".
  • NRG

    Сообщения: 4761 Репутация: N Группа: в ухо

    Spritz 8 сентября 2009 г. 20:05, спустя 1 минуту 53 секунды

    private function _unfoldTask($entity, &$tasks)

    почему параметр приватного метода приходит по ссылке ?
  • Trej Gun

    Сообщения: 5305 Репутация: N Группа: в ухо

    Spritz 8 сентября 2009 г. 20:26, спустя 21 минуту 15 секунд

    NRG, а в какой нотации сказано что так делать незя?
  • NRG

    Сообщения: 4761 Репутация: N Группа: в ухо

    Spritz 8 сентября 2009 г. 20:28, спустя 1 минуту 22 секунды


    NRG, а в какой нотации сказано что так делать незя?
    я думал это стало неписанным правилом с тех пор как появился 5-й пхп …
  • Trej Gun

    Сообщения: 5305 Репутация: N Группа: в ухо

    Spritz 8 сентября 2009 г. 20:37, спустя 9 минут 1 секунду

    я тебе 100-500й раз говорю в php5 не везде объекты передаются по ссылке

    foreach ($obj_array as $name => &$obj)


    вот тебе яркий пример. убери ссылку и посмотри что будет
  • NRG

    Сообщения: 4761 Репутация: N Группа: в ухо

    Spritz 8 сентября 2009 г. 20:50, спустя 13 минут 6 секунд

    CTAPbIu_MABP, ты согласен что твой пример с тем что указал я чуть-чуть разные вещи ?
    и то что там приходит массив а не обьект ты не обратил внимания ?
  • artoodetoo

    Сообщения: 5147 Репутация: N Группа: в ухо

    Spritz 8 сентября 2009 г. 20:56, спустя 5 минут 51 секунду



                           array('moderator',            'allow',      'post management'),
                           array('banned',   'deny',    'user management'),


    Зачем отдельно хранить записи о том что "разрешено" и "не разрешено"? Т. е. почему бы просто не указывать для каждой роли список разрешенных действий. Если действия нет в списке разрешенных, оно считается "deny".

    Подмывает ответить "сам попробуй" :) Для плоского одномерного списка прав этого и правда было бы достаточно.

    В RBAC одна конечная операция может быть частью нескольких ролей пользователя и если она разрешена в нескольких, но запрещена хотябы в одной из них, то она запрещена.
    Это "пессиместическая" модель. Можно использовать и "оптимистическую" — там с точностью до наоборот. В архиве есть файл test1.php где можно поиграть с порядком allow/deny.
    ιιlllιlllι унц-унц
  • artoodetoo

    Сообщения: 5147 Репутация: N Группа: в ухо

    Spritz 8 сентября 2009 г. 21:02, спустя 5 минут 59 секунд

    NRG, PHP 5 не запрещает ссылки. Дело в том, что незадолго до написания этого примера я тестировал передачу массивов в качестве параметров. Теперь руки сами ставят амперсанд.
    ιιlllιlllι унц-унц
  • phpdude

    Сообщения: 26646 Репутация: N Группа: в ухо

    Spritz 8 сентября 2009 г. 21:03, спустя 1 минуту 11 секунд


    NRG, PHP 5 не запрещает ссылки. Дело в том, что незадолго до написания этого примера я тестировал передачу массивов в качестве параметров. Теперь руки сами ставят амперсанд.
    по таким рукам бить надо тапком =))
    Сапожник без сапог
  • Trej Gun

    Сообщения: 5305 Репутация: N Группа: в ухо

    Spritz 8 сентября 2009 г. 21:05, спустя 2 минуты 36 секунд

    твой пример с тем что указал я чуть-чуть разные вещи

    один хуй! пхп гавно!!!

    приходит массив а не обьект

    я не думал что все так глухо
    Спустя 26 сек.
    тестировал передачу массивов в качестве параметров

    потестируй sql запросы
    Спустя 26 сек.
    обращение к файлам и работу с сокетами…
    Спустя 19 сек.
    и ты поймешь что
    пхп гавно!!!
  • phpdude

    Сообщения: 26646 Репутация: N Группа: в ухо

    Spritz 8 сентября 2009 г. 21:08, спустя 2 минуты 30 секунд

    CTAPbIu_MABP, +100
    Сапожник без сапог
  • phpdude

    Сообщения: 26646 Репутация: N Группа: в ухо

    Spritz 8 сентября 2009 г. 21:12, спустя 4 минуты 34 секунды

    artoodetoo, я такую хуйню обычно говнокодом называю
    Сапожник без сапог
  • Trej Gun

    Сообщения: 5305 Репутация: N Группа: в ухо

    Spritz 8 сентября 2009 г. 21:15, спустя 2 минуты 6 секунд

  • artoodetoo

    Сообщения: 5147 Репутация: N Группа: в ухо

    Spritz 8 сентября 2009 г. 21:21, спустя 6 минут 46 секунд

    убрал говнокод из-за охуенных загадок с результатами
    Спустя 185 сек.
    CTAPbIu_MABP, что это?
    Спустя 51 сек.
    а, уже поправил ссыль
    Спустя 176 сек.
    один хуй причем здесь конь. парни, вы меня сильно расстраиваете.
    ιιlllιlllι унц-унц

Пожалуйста, авторизуйтесь, чтобы написать комментарий!