построение «двусторонней» ОО динамической системы ACL - PullRequest
16 голосов
/ 13 сентября 2011

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

В моем приложении разрешения предоставляются динамически, например: пользователь получает разрешения на просмотр действия, потому что он является членом команды, с которой связано действие. Это основано на предположении, что у вас всегда есть Employee (пользователь), который хочет выполнить действие (просмотр / редактирование / и т. Д.) Над 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's 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:

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

Ответы [ 4 ]

1 голос
/ 16 сентября 2011

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

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

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

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

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

0 голосов
/ 22 сентября 2011

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

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

Тогда вы можете вычислить это в функции хранимой процедуры / udf, если хотите, чтобы в БД не извлекались все, или создавали таблицу / список ACL, или разрешение Role, связанное с объектами, которые вы хотите им датьpermissions.

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

0 голосов
/ 22 сентября 2011

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

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

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

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

0 голосов
/ 22 сентября 2011

Возможно, вы захотите рассмотреть подход CakePHP к этой проблеме:

http://book.cakephp.org/view/1543/Simple-Acl-controlled-Application

...