In-Portal Developers Guide

This is a wiki-based Developers Guide for In-Portal Open Source CMS. The purpose of this guide is to provide advanced users, web developers and programmers with documentation on how to expand, customize and improve the functionality and the code the In-Portal software. Please consider contributing to our documentation writing effort.

K4:Использование прав доступа

From In-Portal Developers Guide

Revision as of 15:30, 25 December 2008 by Alex (Talk)
(diff) ←Older revision | Current revision (diff) | Newer revision→ (diff)
Jump to: navigation, search
Компоненты Компоненты
Статьи в этой категории

Для разделения прав доступа в К4 применяются права доступа (permissions). В данной статье описаны основные концепции системы прав доступа, принципы работы с ней и области её применения.

Contents

Зачем нужны права доступа?

На заре существования K4 глобальная система разделения доступа отсутствовала, а для отдельных случаев, при необходимости, писалась простая, специфичная для конкретного проекта система. Была система управления правами для In-Portal, работающая только для отдельных items. А в целом система оставалась легко поддающейся взлому. Стоило только знать названия некоторых событий и принцип построения имён полей на форме.

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

Работа с правами доступа

Задание списка прав в unit config

Самое первое, что следует сделать для начала работы с правами - задать в unit config следующие ключи:

  • PermSection - ключ первого уровня, в значении которого нужно задать массив с указаниями секций, на которые будут проверяться права доступа. У каждого элемента массива ключ означает тип секции, а значение её название. В платформе повсеместно используется только тип секции "main", а другие другие типы секций (search, email, custom) используются только в In-Portal. Начинается название секции проверки прав доступа (как и собственно всех секций, определённых в модуле "custom") с названия модуля и двоеточия, например custom:.
  • permissions - ключ в массиве задания секции. Определяет список прав, связанных с данным unit config. Обычно содержит стандартные виды прав (view, add, edit, delete), а также может содержать индивидуальные права (начинаются с advanced:).

Следующая выдержка из unit config демонстрирует объявление данных ключей:

'PermSection' => Array ('main' => 'custom:phones'),
 
'Sections' => Array (
	'custom:phones' => Array (
		'parent'		=>	'custom',
		'icon'			=>	'custom:phones',
		'label'			=>	'la_tab_Phones',
		'url'			=>	Array('t' => 'custom/phones/phone_list', 'pass' => 'm'),
		'permissions'		=>	Array('view', 'add', 'edit', 'delete', 'advanced:change_price', 'advanced:edit_phone_sale_info'),
		'priority'		=>	1,
		'type'			=>	stTREE,
	),
),

Назначение прав пользователям

Форма редактирования прав доступа группы
Форма редактирования прав доступа группы

Права доступа в К4 распределяются с помощью групп пользователей. Каждый пользователь системы назначается в одну или несколько групп. В свою очередь для каждой группы возможно задать собственный набор прав, которыми состоящие в ней пользователи будут обладать. В платформе данная возможность присутствует только при включённой конфигурационной переменной AdvancedUserManagement (находится в секции Configuration -> System Configuration). Для редактирования доступных прав группы пользователя следует открыть редактирование выбранной группы пользователей (в секции User Management -> Groups) и перейти на вкладку Permissions. Описанная выше форма изображена справа.

Каждая строка формы соответствует секции в дереве. В качестве вспомогательной информации при включенном DEBUG_MODE под именем каждой секции в квадратных скобках отображается её название, под которым она была объявлена в unit config, а также префикс данного unit config. В форме отображаются стандартные права доступа. При нажатии на ссылку Change в столбце Additional открывается форма редактирования расширенных прав доступа (тех, чьи названия начинаются с "advanced:"):

Редактирование расширенных прав доступа группы
Редактирование расширенных прав доступа группы


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

Для редактирование прав, не относящихся ни к одной конкретной секции, используется самая главная (корневая) секция.

Делегирование прав доступа

Если требуется, чтобы для конкретного префикса права доступа определялись правами, назначенными другому префиксу, следует использовать ключ SectionPrefix. Данный ключ задаётся на верхнем уровне unit config. В случае, когда он присутствует, ключ PermSection задавать не нужно, т.к. соответствующая секция будет определяться по префиксу, указанному в ключе SectionPrefix.

Использование прав доступа

Связывание прав и событий

Права доступа проверяются также для запуске событий (events), инициированных по данным запроса к серверу (например, если в запросе присутствует переменная вида prefix_event). Чтобы связать событие с правом(-ами) доступа, используется метод kDBEventHandler::mapPermissions(). В данном методе объявляется массив, определяющий права доступа для событий.

function mapPermissions()
{
	parent::mapPermissions();
	$permissions = Array (
		'OnViewPhone' => Array ('self' => 'view', 'subitem' => 'view'),
		'OnGetPhoneCatalog' => Array ('self' => true),
		'OnModifyPhoneInfo' => Array ('self' => 'edit', 'subitem' => 'add|edit'),
		'OnChangePrice' => Array ('self' => 'advanced:change_price'),
	);
 
	$this->permMapping = array_merge($this->permMapping, $permissions);
}

При проверке прав доступа для события массив kDBEventHandler::permMapping обрабатывается по следующим правилам:

  • находится ключ с именем события у обработчика событий, объявленного в unit config у префикса, для которого событие вызывается;
  • если данный префикс является главным (в конкретном случае, так как один и тот же префикс может быть и главным, и подчинённым в разных ситуациях), то получается значение элемента с ключом self;
  • если префикс является подчинённым, то получается значение ключа subitem;
  • полученное право доступа проверяется всё время у самого главного префикса;
  • если в полученном значение имеется несколько названий прав доступа, разделённых символом "|", то для получения доступа пользователю необходимо иметь по крайней мере одно из указанных прав;
  • если в результате получается значение true, проверка прав доступа не производится и событие всегда получает доступ;
  • если в результате получается значение false, проверка прав доступа не производится и событие никогда не получает доступ (на практике не используется).
Image:Tipbox Icon.gif В вышеупомянутом массиве должны присутствовать связки для каждого события, вызываемого по данным запроса к серверу! Если таковых не будет найдено, то попытка вызова события закончится сообщением об ошибке.

Дополнительные проверки прав доступа

Также дополнительно осуществляются приведённые ниже проверки.

  • Только для пользовательской части сайта из события OnItemBuild вызывается проверка view права (т.е. права на просмотр данных). Данная проверка происходит только тогда, когда в запросе, полученном сервером присутствует ID объекта и объект пытается использовать его для получения данных из базы данных.

Проверка прав доступа из PHP-кода

Система прав доступа имеет возможность проверки прав из произвольного места PHP-кода (напр. событий или тэгов). Для вызова проверки права доступа из PHP-кода следует использовать конструкцию следующего вида:

if ($this->Application->CheckPermission('custom:phones.advanced:change_price')) {
	$object->SetField('Price', $new_price);
}
else {
	$object->SetError('Price', 'cannot_change_price', 'la_error_YouDontHavePermissionToChangePrice');
}

Метод kApplication::CheckPermission имеет следующие параметры:

параметр описание
$name (string) Название права доступа, которое будет проверяется. Название права доступа состоит из 2 частей:
  • название секции (напр. "custom:phones");
  • название вида права, котрое будет проверяться (напр. "edit" или "advanced:change_price").

В названии права название секции отделяется от вида права при помощи символа точки ".", напр. "custom:phones.advanced:change_price".

$type (int) Тип. Может принимать значение 1 (системное право) или 0 (право на категорию), по умолчанию принимает значение 1.
$cat_id (int) ID категории, для которой нужно проверить право доступа; имеет смысл передавать только в случае, когда значение параметра $type равно 0.
Image:Tipbox Icon.gif Если секция в названии права доступа совпадает с секцией префикса, от которого произошло событие, то для получения названия секции нужно использовать метод kEvent:getSection (т.е. следующий код в событии $event->getSection();.

Индивидуальная проверка

Для того, чтобы проверить права доступа на выполнение события нестандартным способом требуется переписать метод kDBEventHandler::CheckPermission (главное в переписанном методе не забыть вызвать родительской метод). Следует обратить особое внимание на то, что данный метод вызывается до выполнения запрашиваемого пользователем события и объект, участвующий в событии ещё не инициализирован данными запроса к серверу. Также следует помнить о том, что в практике переписывание методов проверки прав доступа действительно нужно очень редко, и большинство проблем можно решить, используя стандартные механизмы.

Проверка прав доступа из шаблонов

Также предусмотрена возможность проверки прав доступа в шаблонах используя тэг m_CheckPermission:

<inp2:m_if check="m_CheckPermission" permissions="custom:phones.advanced:change_price" system="1">
	<inp2:m_RenderElement name="inp_edit_box" prefix="request" field="Price" title="la_fld_Price"/>
<inp2:m_else />
	<inp2:m_RenderElement name="inp_label" prefix="request" field="Price" title="la_fld_Price"/>
</inp2:m_if>

Тэгу m_CheckPermission передаются следующие параметры:

название описание
perm_event (string) Название события, наличие права вызова которого требуется проверить. Событие задаётся в форме "prefix_special:EventName", напр. "phone:OnChangePrice".
perm_prefix (string) Префикс объекта, чья основная (primary) категория должна использоваться в процессе проверки прав доступа. Если требуется явно указать ID категории, это можно сделать используя параметр cat_id.
permissions (string) Названия прав доступа, наличие которых нужно проверить. Права можно разбивать на группы, в таком случае:
  • права в пределах одной группы проверяются по AND принципу;
  • права между группами проверяются по OR принципу.

Для объединения прав в группу используется запятая (","). Для объединения групп используется вертикальная черта ("|"). Это будет легче понять на следующем примере:

строка результат
А,B|C,D,E (А AND B) OR (C AND D AND E)
A,B A AND B
A|B,E A OR (B AND E)
A|B|D A OR B OR D
system (int) Указывает на то, что проверяемые права являются системными, т.е. не зависят от текущей категории.
Image:Tipbox Icon.gif По умолчанию проверяются не системные права.
cat_id (int) ID категории, для которой нужно проверить право доступа. Имеет смысл передавать только в случае, когда значение параметра system равно 0, либо параметр system не передан.

Примеры использования тэга m_CheckPermission:

<inp2:m_if check="m_CheckPermission" permissions="custom:phones.advanced:change_price|custom:phones.view" system="1">...
<!-- разрешено ли хотя бы одно из прав custom:phones.advanced:change_price и custom:phones.view -->
 
<inp2:m_if check="m_CheckPermission" permissions="custom:phones.advanced:change_price,custom:phones.view" system="1">...
<!-- разрешены ли оба права -->
 
<inp2:m_if check="m_CheckPermission" permissions="custom:phones.advanced:change_price,custom:phones.view|custom:phones.edit" system="1">...
<!-- разрешены ли одновременно права custom:phones.advanced:change_price и custom:phones.view, или разрешено ли право custom:phones.edit -->
 
<inp2:m_if check="m_CheckPermission" permissions="custom:phones.advanced:change_price">...
<!-- разрешено ли право custom:phones.advanced:change_price в текущей категории (не передан параметр system) -->
 
<inp2:m_if check="m_CheckPermission" permissions="custom:phones.advanced:change_price" perm_prefix="product">...
<!-- разрешено ли право custom:phones.advanced:change_price в текущей категории;
текущую категорию определить по тому, в какой категории находится текущий объект с prefix'ом product -->
 
<inp2:m_if check="m_CheckPermission" permissions="custom:phones.advanced:change_price" cat_id="5">...
<!-- разрешено ли право custom:phones.advanced:change_price в категории c id=5 -->
 
<inp2:m_if check="m_CheckPermission" perm_event="phone:OnChangePrice">...
<!-- разрешен ли запуск события OnChangePrice для prefix'a phone  -->

Системные права и права категорий

Форма редактирования прав доступа для категории
Форма редактирования прав доступа для категории

В K4 реализовано два типа прав доступа:

  • системные права - задаются глобально, определяют право вне зависимости от категории в которой находиться пользователь, используются чаще;
  • права категорий - задаются отдельно для каждой категории, проверяются, исходя из текущей категории; актуально для In-Portal'a.

Права категории можно назначить, открыв редактирование категории и перейдя на закладку Permissions. Затем, выбрав группу пользователей в верхней части и модуль в нижней, можно задать соответствующие права доступа. Галочка в колонке Access определяет наличие права. При отметке галочки Inherited право наследуется у родительской категории, а в колонке Inherited From выведена категория, с которой будет наследовано право в случае отметки галочки Inherited. Данная форма доступна только в In-Portal'e.