Битовые маски для разграничения доступа

Обсуждение документации. Переводы Cookbook и авторские рецепты.
Ответить
Arnowt
Сообщения: 182
Зарегистрирован: 2013.09.13, 11:11

Битовые маски для разграничения доступа

Сообщение Arnowt »

Немного предисловия :)

Есть у меня куча разрозненных сервисов, и давнее желание собрать это все в один сайт.
И вот процессе освоения yii воплощения вышеупомянутого желания, родилась мысль написать "новую" концепцию распределения доступов, не на основе ролей, а на основе индивидуальных разрешений, но при этом пользователей предполагается большое число.

В качестве отвлеченно примера можно привести больницу.
Врач специалист в своей области + у него может быть несколько дополнительных специализаций.
Ему необходимо предоставить доступ во все разделы, в которых он является специалистом.
Если действовать классически, то придется прописывать каждому врачу доступ в каждый раздел.
Если подойти на основе ролей, то со временем кол-во ролей может начать стремится к 2 в степени кол-ва специализаций.
Потому решил сделать так, возможно что не ново для рецептов, тогда можно просто снести тему, а возможно кому и пригодится.

Что не нравится самому:
  • return (object)$val
  • 'access'=>5),//CONTACT+PICTURE Не получилось сделать 'access'=>self::CONTACT| self::PICTURE
Если подключить БД то все будет красиво, но для рецепта работа с БД уже есть.
Хотя тогда все можно делать на checkbox'ах и будет вообще красиво :)

Код: Выделить всё

class User extends CModel{
    //Accesses
    const NOTHING=0;
    const CONTACT=1;
    const MUSIC  =2;
    const PICTURE=4;
    const TEXT   =8;
    const ALL    =0xFFFFFFFF;

    static private $users=array(
            array('id'=>1,'userName'=>'Admin','pass'=>'123','access'=>self::ALL),
            array('id'=>2,'userName'=>'Petr', 'pass'=>'123','access'=>5),//CONTACT+PICTURE
        );

    /**
     * @param $username
     * @return null|object
     */
    static function getUserByName($username){
        foreach(self::$users as $val){
            if($val['userName']==$username) return (object)$val;
        }
        return null;
    }
    /**
     * @param $id
     * @return null|object
     */
    static function getUserById($id){
        foreach(self::$users as $val){
            if($val['id']==$id) return (object)$val;
        }
        return null;
    }
    function attributeNames(){return array();}
}

Код: Выделить всё

class UserIdentity extends CUserIdentity{

    private $_id;

    public function authenticate(){
        $user=User::getUserByName($this->username);
        if($user===null) $this->errorCode=self::ERROR_USERNAME_INVALID;
        elseif($user->pass!==$this->password)$this->errorCode=self::ERROR_PASSWORD_INVALID;
        else{
            $this->_id=$user->id;
            $this->username=$user->userName;
            $this->errorCode=self::ERROR_NONE;
        }
        return !$this->errorCode;
    }
    public function getId(){ return $this->_id; }
}

Код: Выделить всё

class WebUser extends CWebUser{
    private $_model=null;
    function getAccess(){
        if($user=$this->getModel()){
            return $user->access;
        }
    }

    private function getModel(){
        if(!$this->isGuest && $this->_model===null){
            $this->_model=User::getUserById($this->id);
        }
        return $this->_model;
    }
}

Код: Выделить всё

public function accessRules(){
        return array(
            array('allow',
                'actions'=>array('picture'),
                'expression'=>'$user->access & User::PICTURE'),
            array('allow',
                'actions'=>array('text'),
                'expression'=>'$user->access & User::TEXT'),
            array('deny', 'users'=>array('*')),
        );
    }
В общем-то ничего экстраординарного просто сам принцип логического сложения прав может оказаться более приемлемым в некоторых случаях.
Последний раз редактировалось Arnowt 2013.10.21, 17:08, всего редактировалось 1 раз.
Аватара пользователя
yiijeka
Сообщения: 3103
Зарегистрирован: 2012.01.28, 09:14
Откуда: Беларусь
Контактная информация:

Re: Разграничение доступа по разделам.

Сообщение yiijeka »

Хм, а на практике пробовали? У меня например 30+ операций на сайте, 7 ролей, я даже боюсь подумать как это записать в константы, да и потом выудить, что конкретно доступно данному человеку, без включения математического раздела мозга )

И где-то мне что-то подобное попадалось...
p.s. нашёл http://habrahabr.ru/post/130427/
Arnowt
Сообщения: 182
Зарегистрирован: 2013.09.13, 11:11

Re: Разграничение доступа по разделам.

Сообщение Arnowt »

Да, конечно пробовал, собственно сейчас делают проектик, на этом принципе разграничения доступа. Все работает.
если у вас 30+ операций то будет 30+ констант.
Но операции можно группировать, например если разрешено редактирование, то нет смысла делать отдельное разрешение на удаление.

>>да и потом выудить, что конкретно доступно данному человеку
Как же... все его права прописаны в одном месте.
Можно еще их хранить в БД, извлекая их в форму тоже не будет никаких проблем с визуализацией доступов :)
Аватара пользователя
yiijeka
Сообщения: 3103
Зарегистрирован: 2012.01.28, 09:14
Откуда: Беларусь
Контактная информация:

Re: Разграничение доступа по разделам.

Сообщение yiijeka »

Как я понял:

Код: Выделить всё

'expression'=>'$user->access & User::PICTURE'),
Вернёт 0 (на php равнозначно false), если нет пересечения в битах, в противном возвращает различные значения (равнозначно не !false, т.е true)?

Чтобы проверить доступ используем:

Код: Выделить всё

protected function checkAccess($flag)
  {
    return (($user->access & $flag) == $flag);
  }
Чтобы добавить или убрать операцию используем:

Код: Выделить всё

protected function setFlag($flag, $boolVal)
  {
    if($boolVal)
    {
      $user->access |= $flag;
    }
    else
    {
      $user->access &= ~$flag;
    }
  }
Блин непривычно это всё... Спасибо за рецепт, может когда-нибудь и применю)
Аватара пользователя
yiijeka
Сообщения: 3103
Зарегистрирован: 2012.01.28, 09:14
Откуда: Беларусь
Контактная информация:

Re: Разграничение доступа по разделам.

Сообщение yiijeka »

Ещё вопрос, а как интерактивно добавлять нужные операции?
Это нужно найти максимальную операцию и сдвинуть её влево? ($db->max << 1)
Просто я привык к подпиленному под себя http://www.yiiframework.com/extension/auth/ . Интерфейс для создания, удаления, присвоения очень удобен. И данный рецепт пока не укладывается в голове, т.к. операции в константах и руками добавляются в код.

И ещё настораживает:
Внимание
Не используйте сдвиг вправо более чем на 32 бита на 32-битных системах. Не используйте сдвиг влево для получения чисел, требующих для записи более 32 бит. Используйте функции из расширения gmp для побитовых операций над числами, большими чем PHP_INT_MAX.
Arnowt
Сообщения: 182
Зарегистрирован: 2013.09.13, 11:11

Re: Разграничение доступа по разделам.

Сообщение Arnowt »

>>Вернёт 0 (на php равнозначно false), если нет пересечения в битах, в противном возвращает различные значения (равнозначно не !false, т.е true)?

верно. возвращать будет 0 или 1
если сделать так:
'expression'=>'$user->access & (User::PICTURE|User::MUSIC)'); //требуется одно из
то могут быть значения отличные от 0/1 но собственно все что не ноль будет разрешением.

'expression'=>'($user->access & User::PICTURE) & ($user->access & User::MUSIC)'; //требуются оба но это скорее уже экзотика

----------------------------------------------------------
>>return (($user->access & $flag) == $flag);

мне кажется достаточно
return (($user->access & $flag));
Arnowt
Сообщения: 182
Зарегистрирован: 2013.09.13, 11:11

Re: Разграничение доступа по разделам.

Сообщение Arnowt »

>>Это нужно найти максимальную операцию и сдвинуть её влево? ($db->max << 1)
если я вас правильно понял, то Вам тогда придется перепрописать права всем пользователям.
Проще добавлять новые разрешения слева.

Условно если смотреть в двоичной системе смотреть:

Код: Выделить всё

const CONTACT=0b1; //1
const MUSIC=0b10; //2
const PICTURE=0b100; //3
const TEXT=0b1000; //4
и так далее
0b10000 //8
0b100000 //16  
Последний раз редактировалось Arnowt 2013.10.21, 16:00, всего редактировалось 1 раз.
Аватара пользователя
yiijeka
Сообщения: 3103
Зарегистрирован: 2012.01.28, 09:14
Откуда: Беларусь
Контактная информация:

Re: Разграничение доступа по разделам.

Сообщение yiijeka »

const NOTHING=0;
const CONTACT=1;
const MUSIC =2;
const PICTURE=4;
const TEXT =8;
const ALL =0xFFFFFFFF;


Т.е. ($db->max << 1) это я от TEXT пляшу и будет 16 следующим значением для операции, а вы предлагаете от ALL и сдвигать вправо? А так можно же "доуменьшаться" до NOTHING и будет предел? Что тогда?
Arnowt
Сообщения: 182
Зарегистрирован: 2013.09.13, 11:11

Re: Разграничение доступа по разделам.

Сообщение Arnowt »

ALL это частный случай. он известен заранее исходя из разрядности разрешений + резерв как в случае примера.
ALL=0b11111111111111111111111111111111;

Если все же их расширить то ALL тоже изменится там нужно будет добавить один разряд.
То есть перепрописать права всем пользователям с правами ALL и то если это действительно необходимо.

А если умножать на 2 как вы предлагаете, то нужно будет перепрописывать права абсолютно всех пользователей.
Аватара пользователя
yiijeka
Сообщения: 3103
Зарегистрирован: 2012.01.28, 09:14
Откуда: Беларусь
Контактная информация:

Re: Разграничение доступа по разделам.

Сообщение yiijeka »

Ок )
И ещё возник вопрос, например у меня 100500 пользователей, тегов к посту, чего угодно и мне нужно выбрать всех тех, кому доступна админка или пост, у которого есть определённый тег. Это получается, что теперь вместо быстрого поиска по индексу, нужно выбрать все данные и пройтись по каждой записи с проверкой?
Arnowt
Сообщения: 182
Зарегистрирован: 2013.09.13, 11:11

Re: Разграничение доступа по разделам.

Сообщение Arnowt »

WHERE (`access` & $mask)
Аватара пользователя
yiijeka
Сообщения: 3103
Зарегистрирован: 2012.01.28, 09:14
Откуда: Беларусь
Контактная информация:

Re: Разграничение доступа по разделам.

Сообщение yiijeka »

nice, many tnk )
Аватара пользователя
yiijeka
Сообщения: 3103
Зарегистрирован: 2012.01.28, 09:14
Откуда: Беларусь
Контактная информация:

Re: Разграничение доступа по разделам.

Сообщение yiijeka »

Расширение для Yii не пробовали публиковать? И тему текущую лучше переназвать, а то название не явное - "Побитовые маски для разграничения доступа"
Arnowt
Сообщения: 182
Зарегистрирован: 2013.09.13, 11:11

Re: Разграничение доступа по разделам.

Сообщение Arnowt »

Я самим yii не владею достаточно чтобы расширениями заниматься :)
Переназвать можно.
Ответить