Контрол на достъпа (оторизация)
Оторизацията определя дали потребителят има достатъчно привилегии, например за достъп до определен ресурс или за извършване на действие. Оторизацията предполага успешно удостоверяване, т.е. че потребителят е влязъл в системата.
В примерите ще използваме обект от клас Nette\Security\User, който представлява
текущия потребител и който получавате, като го предавате чрез инжектиране на зависимости. В
презентаторите просто се обадете на $user = $this->getUser()
.
За много прости сайтове с администрация, където правата на
потребителите не са диференцирани, можете да използвате вече познатия
метод isLoggedIn()
като критерий за оторизация . С други думи: щом
даден потребител влезе в системата, той има права за всички действия и
обратно.
Роли
Целта на ролите е да предлагат по-прецизно управление на правата и да
останат независими от потребителското име. Веднага след като
потребителят влезе в системата, на него се присвояват една или повече
роли. Самите роли могат да бъдат прости низове, например admin
,
member
, guest
и т.н. Те се посочват във втория аргумент на
конструктора SimpleIdentity
, като низ или масив.
Като критерий за оторизация ще използваме метода isInRole()
, който
проверява дали потребителят е член на дадена роля:
Както вече знаете, излизането от системата не заличава самоличността
на потребителя. Така че методът getIdentity()
все още връща обекта
SimpleIdentity
, включително всички предоставени роли. Рамката Nette Framework
се придържа към принципа „по-малко код, повече сигурност“, така че при
проверка на ролите не е необходимо да се проверява дали потребителят е
влязъл в системата. Методът isInRole()
работи с ефективни роли,
т.е. ако потребителят е влязъл в системата, се използват ролите,
присвоени на лицето, а ако не е влязъл в системата, вместо това се
използва автоматичната специална роля guest
.
Възложител
В допълнение към ролите ще въведем термините ресурс и операция:
- роля е атрибут на потребителя – например модератор, редактор, посетител, регистриран потребител, администратор, …
- Ресурс е логическа единица на приложение – например статия, страница, потребител, елемент от менюто, проучване, водещ, …
- Операция е конкретно действие, което потребителят може или не може да извърши върху ресурс – разглеждане, редактиране, изтриване, гласуване, …
Упълномощителят е обект, който решава дали дадена роля има
разрешение да извърши определена операция с определен ресурс.
Това е обект, който имплементира интерфейса Nette\Security\Authorizator с
един-единствен метод isAllowed()
:
Добавяме оторизатор към конфигурацията като услуга на контейнер DI:
По-долу е даден примерен случай на употреба. Обърнете внимание, че
този път се извиква методът Nette\Security\User::isAllowed()
, а не методът
authorizer, така че няма първи параметър $role
. Този метод извиква
MyAuthorizator::isAllowed()
последователно за всички потребителски роли и
връща true, ако поне една от тях има разрешение.
И двата аргумента са незадължителни и по подразбиране са all.
Разрешения на ACL
Nette идва с вградена реализация на оторизатор, класът Nette\Security\Permission, който предлага лесен и гъвкав ACL (Access Control List) слой за контрол на разрешенията и достъпа. Когато работим с този клас, определяме роли, ресурси и индивидуални разрешения. Ролите и ресурсите обаче могат да образуват йерархии. За да обясним това, ще покажем пример за уеб приложение:
guest
: посетител, който не е влязъл в системата, но има право да чете и разглежда публичната част на сайта, т.е. да чете статии, да коментира и да гласува в анкети.registered
: влязъл в системата потребител, който може да оставя и коментари.admin
: можете да управлявате статии, коментари и анкети
И така, определихме определени роли (guest
, registered
и
admin
) и споменахме ресурсите (article
, comments
, poll
),
до които потребителите могат да имат достъп или да предприемат
действия (view
, vote
, add
, edit
).
Създаваме инстанция на класа Permission и дефинираме роли. Можем да
използваме наследяване на роли, което гарантира, че например
потребител с роля admin
може да прави това, което може да прави
обикновен посетител на сайта (и със сигурност повече).
Сега ще дефинираме списък с ресурси, до които потребителите имат достъп:
Ресурсите могат да използват и наследяване, например можем да
добавим $acl->addResource('perex', 'article')
.
А сега най-важната част. Ще определим правила между тях, определящи кой какво може да прави:
Какво да правим, ако искаме да предотвратим достъпа на някого до даден ресурс?
След като вече сме създали набор от правила, можем просто да зададем заявки за разрешение:
Същото важи и за регистриран потребител, но той също може да коментира:
Администраторът може да редактира всичко, освен анкетите:
Разрешенията могат да бъдат оценявани динамично, като можем да оставим решението на нашето собствено обратно извикване, на което се предават всички параметри:
Но как да решим ситуацията, в която имената на ролите и ресурсите не
са достатъчни, т.е. искаме да определим, че например ролята registered
може да редактира ресурса article
само ако е негов автор? Ще
използваме обекти вместо низове, като ролята ще бъде обектът Nette\Security\Role и източникът Nette\Security\Resource. Техните методи
getRoleId()
и getResourceId()
ще върнат изходните низове:
Сега нека създадем правило:
ACL се заявява чрез предаване на обекти:
Една роля може да бъде наследена от една или повече други роли. Но какво се случва, ако при един от предците определено действие е разрешено, а при друг – забранено? След това влиза в сила теглото на ролята – последната роля в масива от наследени роли има най-голямо тегло, а първата – най-малкото:
Ролите и ресурсите също могат да бъдат премахвани (removeRole()
,
removeResource()
), правилата също могат да бъдат отменяни
(removeAllow()
, removeDeny()
). Връща се масив от всички роли на преки
родители getRoleParents()
. Въпросът дали две същности наследяват една
от друга връща roleInheritsFrom()
и resourceInheritsFrom()
.
Добавяне като услуга
Трябва да добавим създадения от нас ACL към конфигурацията като
услуга, за да може да се използва от обекта $user
, т.е. за да можем да
го използваме в нашия код, например $user->isAllowed('article', 'view')
. За
тази цел ще напишем фабрика за него:
И го добавете към конфигурацията:
След това в хоста можете да проверите разрешенията в метода
startup()
, напр: