ФорумПрограммированиеPHP для идиотов → Управление правами в социальной сети

Управление правами в социальной сети

  • mario

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

    Spritz 12 января 2010 г. 2:47, спустя 24 секунды

    artoodetoo, модель очень хороша… но вот мен интересует вопрос о производительности… касаемо запросов, ведь это необходимо делать будет несколько раз на одну страницу на одного пользователя…  Хотя если продумать модель данных, такую что бы могла брать как из серриализованого массива(кеш, или же какой другой…), из базы (напрямую) и добавить на обновление кеша некие "триггеры" при добавлении/изменении ролей…
    Спустя 213 сек.
    В общем есть у меня другое представление системы управление прав, попробую завтра-послезавтра описать её… По ходу кардинально отличающийся от твоей, но не чего не могу сказать о производительности :)
  • artoodetoo

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

    Spritz 12 января 2010 г. 2:54, спустя 6 минут 41 секунду

    Давай сосредоточимся на выполнении задачи. Вопрос кеширования — отдельная песня.

    "Solve the business case, before optimizing the solution"

    "One of the most common mistakes done even by experienced developers is starting to optimize code without identifying the problem"

    © Ilia Alshanetsky (один из разработчиков PHP)
    ιιlllιlllι унц-унц
  • artoodetoo

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

    Spritz 12 января 2010 г. 3:10, спустя 16 минут 20 секунд

    Поехали гонять запросы.

    Listing 1. Вывод явно назначенных ролей в читабельном виде. Чисто для контроля.
    WHERE (u.is_team=0) AND (c.is_team=1) означает "отношения пользователей к группам"

    SELECT u.short_name AS user_name, c.short_name AS cont_name, l.short_name AS role_name
    FROM x_relations AS e
    INNER JOIN x_contexts AS u ON u.cont_id=e.user_id
    INNER JOIN x_contexts AS c ON c.cont_id=e.cont_id
    INNER JOIN x_roles AS l ON l.role_id=e.role_id
    WHERE (u.is_team=0) AND (c.is_team=1)

    результат:

    user_name cont_name role_name
    durov bobrdobr admins
    mary bobrdobr members
    ivanov bobrdobr members
    john photo-moto admins
    ivanov photo-moto members
    petrov photo-moto members


    Listing 2. У нас должна быть роль по-умолчанию. Это visitors (id=1)
    Переписываем запрос на LEFT JOIN и ifnull()

    SELECT u.short_name AS user_name, c.short_name AS cont_name, l.short_name AS role_name
    FROM x_contexts AS u, x_contexts AS c
    LEFT JOIN x_relations AS e ON (e.user_id = u.cont_id) AND (e.cont_id = c.cont_id)
    INNER JOIN x_roles AS l ON l.role_id = ifnull( e.role_id, 1 )
    WHERE (u.is_team = 0) AND (c.is_team=1)

    результат с непрописанными явно отношениями

    user_name cont_name role_name
    guest bobrdobr visitors
    durov bobrdobr admins
    john bobrdobr visitors
    mary bobrdobr members
    ivanov bobrdobr members
    petrov bobrdobr visitors
    guest photo-moto visitors
    durov photo-moto visitors
    john photo-moto admins
    mary photo-moto visitors
    ivanov photo-moto members
    petrov photo-moto members
    ιιlllιlllι унц-унц
  • Mr.Pihto

    Сообщения: 1386 Репутация: N Группа: Адекваты

    Spritz 12 января 2010 г. 5:29, спустя 2 часа 18 минут 39 секунд

    О_о
  • artoodetoo

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

    Spritz 28 января 2010 г. 8:03, спустя 16 дней 2 часа 34 минуты

    Listing 3. Какова роль пользователя john (id=3) в группе bobrdobr (id=5)
    (см. требование 1)

    SELECT ifnull(e.role_id, 1)
    FROM x_contexts AS c
    LEFT JOIN x_relations AS e ON (e.cont_id=c.cont_id) AND (e.user_id=3)
    WHERE (c.cont_id=5)

    Выведет "1" (т.е. группа visitors). Отношение не описано явно, сработало условие ifnull

    Listing 4. Входит ли john в группу bobrdobr (как участник (2) или администратор (3))
    (см. требование 1)

    SELECT 1
    FROM x_contexts AS c
    JOIN x_relations AS e ON (e.cont_id=c.cont_id) AND (e.user_id=3)
    WHERE (c.cont_id=5) AND (e.role_id IN(2,3))

    вернет пустое множество, т.к. нет, не входит
    если протестируем группу photo-moto …WHERE (c.cont_id=6)… то вернет "1"

    Listing 5. Какие права есть у пользователя john (id=3) в группе bobrdobr (id=5)
    (см. требование 2)

    SELECT r.act_id
    FROM x_contexts AS c
    LEFT JOIN x_relations AS e ON (e.cont_id=c.cont_id) AND (e.user_id=3)
    INNER JOIN x_rights AS r ON (r.cont_id=c.cont_id) AND (r.role_id=ifnull(e.role_id, 1))
    WHERE (c.cont_id=5)

    результат

    act_id
    1
    3
    5

    (вывелись права для роли visitors)

    Listing 6. Есть ли у john право на blog-post в группе bobrdobr?
    (см. требование 2)

    SELECT 1
    FROM x_contexts AS c
    LEFT JOIN x_relations AS e ON (e.cont_id=c.cont_id) AND (e.user_id=3)
    INNER JOIN x_rights AS r ON (r.cont_id=c.cont_id) AND (r.role_id=ifnull(e.role_id, 1))
    WHERE (c.cont_id=5) AND (r.act_id=2)

    вернет пустое множество, т.к. нет у него права.
    если протестируем группу photo-moto …WHERE (c.cont_id=6)… то вернет "1"

    Listing 7a. В каких контекстах john выступает в роли admins?
    (см.требование 3)

    SELECT c.cont_id
    FROM x_contexts AS c
    INNER JOIN x_relations AS e ON (e.cont_id=c.cont_id) AND (e.user_id=3)
    WHERE e.role_id = 3

    выведет 6

    Listing 7b. В каких контекстах john выступает в роли X, с учетом неявно назначаемой роли id=1 visitors?
    (см.требование 3)

    SELECT c.cont_id
    FROM x_contexts AS c
    LEFT JOIN x_relations AS e ON (e.cont_id=c.cont_id) AND (e.user_id=3)
    WHERE ifnull(e.role_id, 1) = :X


    Listing 8. В каких контекстах john может смотреть фотографии (act_id=3)
    (см. требование 4)

    SELECT c.cont_id
    FROM x_contexts AS c
    LEFT JOIN x_relations AS e ON (e.cont_id=c.cont_id) AND (e.user_id=3)
    INNER JOIN x_rights AS r ON (r.cont_id=c.cont_id) AND (r.role_id=ifnull(e.role_id, 1))
    WHERE r.act_id=3

    права мы задавали только для групп, поэтому выведутся две записи "5" и "6"
    для r.act_id=4 'photo-post' выведется только "6"


    Вобщем основные требования выполяются для придуманной структуры. Все проверки можно выразить в SQL.

    Фишка в управлении правами на основе назначаемых ролей + "роль по-умолчанию" + легко расширяемый набор прав на операции.
    ιιlllιlllι унц-унц
  • artoodetoo

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

    Spritz 12 января 2010 г. 11:18, спустя 3 часа 14 минут 29 секунд


    artoodetoo, модель очень хороша… но вот мен интересует вопрос о производительности… касаемо запросов, ведь это необходимо делать будет несколько раз на одну страницу на одного пользователя…

    Я бы завел такое кеширование: массив id всех контекстов, где роль данного пользователя отличается от роли по-умолчанию. Тогда обращаясь к любой странице можно быстро вычислить роль.
    А если еще для каждого контекста кешировать полный набор допустимых в нем операций по ролям… тогда выяснится что мы засунули в кеш просто все данные о правах :)

    Пока нет замеров на правдоподобно больших данных, рано чего-то оптимизировать, IMHO.

    [tt]OOO!!! 1234[/tt]
    ιιlllιlllι унц-унц
  • artoodetoo

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

    Spritz 12 января 2010 г. 13:07, спустя 1 час 49 минут 33 секунды

    Теперь о PHP.

    Предположим мы фанаты MVC. Контроллер выглядит как класс с методами :) круто-крут-круто!
    Наш роутер анализирует URL и извлекает из него "controller/action" типично для популярных фреймворков. Например вызывается
    GroupController->index() или
    PersonController->newPost()
    Логично предположить, что в начале ЛЮБОГО ПУБЛИЧНОГО метода контроллера надо получить инфу о правах пользователя. Воспользуемся наследованием от общего предка и protected методом


    class CustomController
    {
    protected
    $userId,
    $roleId,
    $contextId,
    $availableActions;

    protected function applyContext()
    {
    $this->userId = Auth::currentUser();

    $this->contextId = Router::$contextId;
    $this->roleId = Context::getRole($this->contextId, $this->userId);
    $this->availableActions = Context::getRights($this->contextId, $this->roleId);

    if (!in_array(Router::$action, $this->availableActions))
    throw new Exception('Access denied');
    }

    }

    // …

    class GroupController extends CustomController
    {
    public function index()
    {
    $this->applyContext();
    // some code
    // …
    Template::render('group-index', $data);
    }

    public function newPost()
    {
    $this->applyContext();
    // some code
    // …
    Template::render('new-post', $data);
    }

    // …

    }


    выше я приводил запросы, которые должны использоваться в Context::getRole($contextId, $userId) и Context::getRights($contextId, $roleId)
    вот как-то так я это вижу…
    ιιlllιlllι унц-унц
  • AndryG

    Сообщения: 237 Репутация: N Группа: Адекваты

    Spritz 27 января 2010 г. 13:49, спустя 15 дней 41 минуту

    Если у нас посты бывают разных типов "нейтраль/гневный/и т.д." и мы хотим через права ограничить добавление постов разных типов.

    Как это встроить в Вашу модель? (я про class CustomController и его потомков)
    Творить методы
    GroupController->newPost_a
    GroupController->newPost_b
    не совсем красиво.
  • mario

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

    Spritz 27 января 2010 г. 15:04, спустя 1 час 15 минут 3 секунды


    Если у нас посты бывают разных типов "нейтраль/гневный/и т.д." и мы хотим через права ограничить добавление постов разных типов.

    Как это встроить в Вашу модель? (я про class CustomController и его потомков)
    Творить методы
    GroupController->newPost_a
    GroupController->newPost_b
    не совсем красиво.

    мне кажется тут необходимо реализовать некоторую связку "рейтинг"+пост, рейтинг в кавычках потому, что не совсем рейтинг.
  • artoodetoo

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

    Spritz 27 января 2010 г. 15:19, спустя 15 минут 8 секунд


    Творить методы
    GroupController->newPost_a
    GroupController->newPost_b
    не совсем красиво.

    некрасиво так называть методы, они должны говорить о сути действия. других некрасивостей не вижу
    ιιlllιlllι унц-унц
  • AndryG

    Сообщения: 237 Репутация: N Группа: Адекваты

    Spritz 27 января 2010 г. 18:04, спустя 2 часа 44 минуты 56 секунд

    Уточню.
    Например у нас посты/тосты бывают трех типов: a,b,c.
    По модели прав надо:
    Вася может постить только типы А и С
    Петя - только B
    Света - А и В

    Ваш код тут дает сбой … он либо дает постить либо нет - нет проработки "кому какого типа посты можно добавлять".

    Вот и спрашиваю Вас: "как эти хотелки прав вставить в Ваш код"?

    Как пример показал один из тупых вариантов - натворить кучу практически одинаковых методов.
  • artoodetoo

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

    Spritz 27 января 2010 г. 19:56, спустя 1 час 52 минуты 7 секунд

    Вот не надо дезинформации! Только что сами писали — "придется завести по методу на создание каждого типа постов". Помоему это логично, пользователь в роли "крутой" может a, b и c, а в роли "так себе" только a.

    В этой системе одно "право" дает ответ Да/Нет. Но почему бы не использовать 3 правила?

    Но если хочется обойтись одним методом для трех разных случаев — делайте, пожалуйста. И в этом одном делайте дополнительные проверки для a, b и c на уровне скрипта. Дело вкуса. Только вы уже не сможете на уровне SQL одним запросом найти список ролей или пользователей, кто может выполнить конкретно "действие a". Помоему ТАК делать некрасиво.

    Мой выбор — создавать по одному правилу для каждого действия. В роль можно включить сколько угодно правил.
    Спустя 216 сек.

    По модели прав надо:
    Вася может постить только типы А и С
    Петя - только B
    Света - А и В


    Уточню не "Вася может". А "Роль крутые мальчиши" может.
    А "Плохие мальчиши" может только "B".
    То есть ваш т.н. "рейтинг" — это Роль.

    Мы присваиваем роли конкретным пользователям. А права раздаем на уровне ролей. Это принципиальный момент!
    Спустя 223 сек.
    Еще раз:

       $this->contextId    = Router::$contextId; // мы определяем контекст для текущего URL
       $this->roleId         = Context::getRole($this->contextId, $this->userId); // Мы находим в какой роли выступает пользователь в данном контексте
       $this->availableActions = Context::getRights($this->contextId, $this->roleId); // Мы получаем набор прав РОЛИ !!!

    Набор прав роли в данном контексте может включать любое сочетание a, b, c и еще черт знает сколько действий.
    ιιlllιlllι унц-унц
  • Абырвалг

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

    Spritz 26 июня 2010 г. 23:23, спустя 150 дней 2 часа 26 минут

    а делал ли ты наследование/включение прав (actions)?

    читать новость
    редактировать новость - включает в себя "читать новость"
  • artoodetoo

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

    Spritz 27 июня 2010 г. 6:34, спустя 7 часов 11 минут 42 секунды

    Здесь — нет. Самоцитата: "для нашей условной социалки можно описать правила попроще, но так, чтобы одним SELECT получить ответ на любой вопрос"

    А вот в теме Эксперименты с RBAC я разрешаю "задачи могут иметь подзадачи".
    ιιlllιlllι унц-унц
  • master

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

    Spritz 27 июня 2010 г. 6:28, спустя 23 часа 53 минуты 39 секунд

    эта поебень называется матричный доступ
    http://ru.wikipedia.org/wiki/Избирательное_управление_доступом
    как в линуксе - rwx отдельно для групп, отдельно для пользователей
    либо делать отдельную таблицу, как artoodetoo, либо в таблицу запией добавлять два поля: права для групп, права для пользователей (можно сериализованным массивом в текстовое поле).

    читать новость
    редактировать новость - включает в себя "читать новость"

    в линухе не включает например
    не всё полезно, что в swap полезло

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