Построение "двухсторонней" динамической системы ACL OO

Этот вопрос возник при разработке выделенной системы ACL для пользовательского приложения, но я думаю, что это относится к системам ACL в целом, так как я не узнал, как решить эту проблему, просмотрев некоторые из основных систем, например Zend_ACL.

В моем приложении разрешения предоставляются динамически, например: пользователь получает разрешения на просмотр для деятельности, потому что он является членом команды, к которой связана деятельность. Это основывается на предположении, что у вас всегда есть Employee (пользователь), который хочет выполнить действие (просмотр/редактирование/etc) на Item (один из объектов в моем приложении, например Activity, Team и т.д.), Этого достаточно для целевого использования;

$Activity = new Activity( $_POST['activity_id'] );

$Acl = new Acl( $Activity );
if ( !$Acl->check( 'edit' ) {
    throw new AclException('no permission to edit');
}

Мой класс Acl содержит все бизнес-правила для предоставления разрешений, и они создаются "на лету" (хотя иногда кэшируются по соображениям производительности);

/**
 * Check the permissions on a given activity.
 * @param Activity $Activity
 * @param int $permission (optional) check for a specific permission
 * @return mixed integer containing all the permissions, or a bool when $permission is set
 */
public function checkActivity( Activity $Activity, $permission = null ) {
    $permissions = 0;

    if ( $Activity->owner_actor_id == $this->Employee->employee_id ) {
        $permissions |= $this->activity['view'];
        $permissions |= $this->activity['remove'];
        $permissions |= $this->activity['edit'];
    } elseif ( in_array( $this->Employee->employee_id, $Activity->contributor_ids_arr ) ) {
        $permissions |= $this->activity['view'];
    } else {
        /**
         * Logged in user is not the owner of the activity, he can contribute 
         * if he in the team the activity is linked to
         */
        if ( $Activity->getTeam()->isMember( $this->Employee ) ) {
            $permissions |= $this->activity['view'];
        }
    }

    return ( $permission ? ( ( $permission & $permissions ) === $permission ) : $permissions );
}

Эта система отлично работает как есть.

Проблема с этим подходом возникает, когда вы хотите "отменить" правила ACL. Например, "выберите все действия, которые мне разрешено редактировать". Я не хочу вводить какую-либо логику, как WHERE owner_actor_id = $Employee->employee_id в код, который нуждается в действиях, потому что это ответственность класса Acl, и он должен оставаться централизованным. В текущей реализации у меня нет другого выбора, чтобы извлекать все действия в коде, а затем утверждать их один за другим. Это, конечно, очень неэффективный подход.

Итак, я ищу некоторые идеи по хорошей архитектуре (или указатель на существующую реализацию ACL или некоторые соответствующие шаблоны проектирования) для создания ACL-системы, которая может как-то сделать как hasPermission( $Item, $permission ), так и fetchAllItems( $permission ), в идеале с тем же набором бизнес-правил.

Спасибо всем заблаговременно!


Я рассмотрел Zend_ACL, но это больше касается общих разрешений. Я также нашел здесь следующие вопросы о SO:

Но, к сожалению, они, похоже, тоже не отвечают на вопрос.

Ответы

Ответ 1

Коллега предложил мне еще один взгляд на этот вопрос, который также может быть решением этой проблемы.

То, что мне казалось нужным, - это поместить весь код, связанный с доступом, в класс ACL (зеркальное отражение моего утверждения о том, что "я не хочу помещать любую логику, как WHERE owner_actor_id = $Employee->employee_id в код, который вам нужен деятельности, поскольку это ответственность класса Acl, и его следует держать централизованным".).

Я действительно хочу, чтобы удостоверился, что пользователь никогда не может получить доступ к тому, что не соответствует правилам, перечисленным в классе ACL. Это не проблема, если "рабочий код" уже получает подмножество данных - если он в конечном счете проверяется на "реальный" ACL. Самое худшее, что может случиться, - это то, что пользователь видит меньше, чем он предполагал, что намного лучше, чем больше.

Используя это "решение" (альтернативный подход, если хотите), вы избегаете сбора всех данных, сохраняя при этом преимущество наличия всех правил в одном месте. Любое другое решение, о котором я мог думать, будет включать дублирование правил, так как для проверки заданного ресурса вам понадобятся правила PHP и правила, написанные в MySQL для получения всех.

По-прежнему возможно, чтобы поместить код выборки подмножества в класс Acl, однако я думаю, что было бы лучше сохранить класс небольшим и сфокусированным (потому что я считаю, что читаемость кода в этом класс также очень важен).

Ответ 3

Насколько я знаю, ACL должен использоваться для проверки общих разрешений. Права на основе сущностей не должны относиться к ACL. Для такой задачи я рассмотрю, как Linux/Unix управляет разрешениями на файлы.

          owner    group   all
read        1        1      1
write       1        0      0
execute     1        0      0
--------------------------------------
            7        4      4

С подобной реализацией разрешения на выборку и проверку просты, но вам нужно добавить еще один слой в ваше приложение.

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

Ответ 4

Похоже, что это больше похоже на контроль доступа на основе ролей или RBAC, чем ACL списка контроля доступа, поскольку вы предоставляете разрешения пользователю на объект, зависит от того, какую роль они имеют, если они находятся в группе, объединенном списке или является владельцем, но вы не сохраняете его как роль, поэтому не можете быть RBAC, но вы не сохраняете разрешения, потому что считаете, что он не может быть ACL.

Итак, если вы хотите запросить группу объектов по разрешению, которое вы принимаете, очевидно, вам нужно сначала вычислить, что назначенные разрешения для их получения.

Затем вы можете вычислить, что в функции хранимой процедуры /udf, если вы хотите, чтобы db не извлекал все или не делал таблицу/список ACL или разрешение на роль, связанную с объектами, которые вы хотите предоставить им.

Надеюсь, что это будет полезно, хороший вопрос, он немного обжарил мой мозг, но теперь мне нужно сделать что-то подобное.. хорошо провести день