Auth - autentizace a autorizace

Systém autentizace a autorizace v Jet se unifikovaná abstraktní rozhraní pro provádění základních operací pro přihlášení uživatele, jeho ověření a kontrolu oprávnění k libovolným operacím.

Co to v praxi znamená? Téměř v každé aplikaci potřebujeme, aby se nějací uživatelé mohli přihlašovat, dále potřebujeme kontrolovat zda jsou přihlášeni, zda je jejich účet platný a pokud ano, tak zda smí provádět právě to co se zrovna provádět chystají. To je vlastně vždy stejné, vždy se jedná o stejné operace s autentizací a autorizací. Co se liší je samotná nízkoúrovňová implementace. Kde jsou například uloženy uživatelské účty a jak? Je to v databázi? Jakou má tabulka strukturu? Nebo údaje o uživatelských účtech vůbec nejsou uloženy v databázi aplikace a přístup se řídí firemní sítí? A co přihlášení pomocí služeb třetích stran? A samozřejmě se může zásadně lišit spektrum informací, které o uživatelích zaznamenáváme a tak dále.

To jak entita uživatel vypadá a kde a jak je uložena a jak se ověřuje její přihlášení je neskutečně variabilní a různorodé. A zároveň musí tedy být možné implementovat cokoliv. Ovšem zbytek aplikace příliš nezajímá zda je uživatel v databázi, nebo někde jinde. Ne, zbytek aplikace prostě potřebuje vědět: uživatel je / není přihlášen, jeho účet je / není platný a tuto operaci smí / nesmí provádět. Tedy ať je v pozadí cokoliv složitého, na povrchu je vlastně jednoduché rozhraní a opět fasáda a kontejner držící kontroler, který už se o vše postará. Z hlediska použití aplikací se tedy jedná o velice jednoduchý systém, který je stručný a jasný. Ale který má za úkol zastřešit cokoliv a neklade žádná omezení.

Pojďme si nejprve říct s čím se v Jet Auth setkáme:

  • Kontroler
    Kontroler řídí autentizační a autorizační logiku.
    Tedy řídí logiku přihlášení, odhlášení, ověření platnosti uživatele a ověření zda uživatel má potřebné oprávnění (viz dále).
    Kontroler reprezentuje rozhraní Jet\Auth_Controller_Interface.
  • Uživatel
    Uživatel je entita, která musí mít několik základních obecných vlastností a metod (například nějaké uživatelské jméno má každý uživatel) a která pochopitelně může být v aplikaci libovolně rozšiřována. Entita se stará o faktické načítání a ukládání informací o uživateli ať jsou data uložena kdekoliv v jakékoliv formě - to už je vnitřní záležitostí entity.
    Reprezentováno rozhraním Jet\Auth_User_Interface.
  • Role
    Oprávnění (k operacím) v Jet jsou řešena systémem rolí. Rolí může být libovolný počet a každý uživatel může být v libovolném množství rolí. (oprávnění pak eskaluje - sčítá se)
    Pochopitelně tedy musí existovat entita role, která má definované základní metody, ale kterou je opět možné v aplikaci libovolně rozšiřovat.
    Reprezentováno rozhraním Jet\Auth_Role_Interface.
  • Oprávnění
    Oprávnění je subentita role a reprezentuje určité oprávnění (k nějaké operaci). Role samozřejmě může mít libovolný počet oprávnění.
    Reprezentováno rozhraním Jet\Auth_Role_Privilege_Interface.
  • Hlavní fasáda
    Právě přes tuto třídu a její statické metody aplikace s autentizačním a autorizačním systémem pracuje.
    Reprezentováno třídou Jet\Auth

Inicializace

Protože je Auth systém pouze prázdný kontejner, tak je nutné jej nejprve "naplnit", tedy říct jaký kontorler má být použit pro řízení logiky. Implementace kontroleru je čistě záležitost aplikace. Tedy v aplikaci musíte mít třídu (v praxi spíše třídy), která implementuje rozhraní Jet\Auth_Controller_Interface.

V Jetu (v knihovně) žádný předpřipravený kontroler nenajdete, ale ukázkové kontrolery jsou součástí ukázkové aplikace. Jde o třídy:

  • JetApplication\Auth_Controller_Admin
  • JetApplication\Auth_RESTClient_User
  • JetApplication\Auth_Controller_Web

Pokud jste již seznámeni s Jet MVC, tak zde určitě již tušíte, že jde o různé kontrolery pro různé báze. Tedy přihlašování do administrace je úplně něco jiného, než přihlášování klienta REST API. A dále jistě tušíte, že půjde o nějaký inicializátor báze a je to přesně tak - hned se k tomu dostaneme.

Žádný Auth kontroler se neinicializuje hned na začátku běhu aplikace. Ne, Jet Auth je nutné inicializovat až ve chvíli, kdy víte jaká je vlastně situace a jaký kontroler budete potřebovat. V případě že používáte Jet MVC, tak se inicializace provede v inicializátoru báze. Tedy například takto:

namespace JetApplication;

use 
Jet\MVC_Router;
use 
Jet\Auth;
// ... ... ... ...
class Application_Admin
{
    
// ... ... ... ...
    
public static function initMVC_Router $router ): void
    
{
        
// ... ... ... ...
        
Auth::setController( new Auth_Controller_Admin() );
        
// ... ... ... ...
    
}

    
// ... ... ... ...
}

Pokud nebudete dělat aplikaci založenou na Jet MVC, pak je na vás, kde inicializaci provedete. Ale vždy je nutné provést něco takového:

Auth::setController( new MyMegaCoolAuthController() );

Použití

Jak již bylo řečeno, tak autentizační a autorizační systém se používá přes fasádu Jet\Auth. Ukažme si to nyní prakticky.

Je někdo přihlášen?

Pokud potřebujete ověřit, zda je nějaký uživatel přihlášen a jeho uživatelský účet je platný (například nedošlo k jeho zablokování), pak stačí udělat toto:

if( Auth::checkCurrentUser() ) {
    
//It's OK
}

Kdo je přihlášen?

Často je třeba zjistit kdo je přihlášený a zjistit o něm nějaké informace:

if( ( $user Auth::getCurrentUser() ) ) {
    echo 
'Current user: '.$user->getUsername();
}

Přihlášení

Máte k dispozici uživatelské jméno a heslo, chcete jej ověřit a pokud jsou údaje správně, pak uživatele přihlásit:

if( Auth::login$username$password ) ) {
    echo 
'Logged in ...';
}

Odhlášení

Na základě nějaké akce (Jet neřeší jaké - to je věc vaší aplikace) je potřeba uživatele odhlásit:

Auth::logout();

Obsluha přihlášení

Uživatel není přihlášen a chcete po kontroleru, aby se postaral o přihlášení. To v praxi znamená zobrazení přihlašovacího formuláře místo obsahu. Jak a co kontroler provede je už čistě věcí kontroleru.

if( Auth::checkCurrentUser() ) {
    
Auth::handleLogin();
}

Kontrola oprávnění - může uživatel provést "to a to"?

Již jsme si řekli, že kontrola oprávnění je řešena formou rolí a jejich práv. K tomu se ještě vrátíme, ale teď si ukažme jak provést ověření, zda uživatel potřebné právo má. To je totiž již velice jednoduché.

V aplikaci máte definované oprávnění pro prohlížení dokumentů a chcete vědět, zda má uživatel oprávnění konkrétní dokument vidět:

if( Auth::getCurrentUserHasPrivilege'view-document'$document_id ) ) {
    
//It's OK
}

To byla kontrola zcela libovolného a vámi definovaného oprávnění. Ovšem Jet má dvě základní oprávnění zabudované:

Oprávnění k návštěvě stránky:

$page MVC::getPage('some-page-id');
if( 
Auth::checkPageAccess$page ) ) {
    
//It's OK
}

Oprávnění k ACL akci aplikačního modulu:

if( Auth::checkModuleActionAccess('Some.Module''module-action') ) {
    
//It's OK
}

Poznámka: Kontrola oprávnění samozřejmě nejprve musí ověřit zda je uživatel přihlášený a jeho účet je aktivní.

Uživatel

Entita uživatel musí mít splňovat minimálně to, co deklaruje rozhraní Jet\Auth_User_Interface. Důležité je, že rozhraním je definováno pouze funkční minimum. Váše implementace uživatele samozřejmě může mít jakékoliv další vlastnosti. Ať je to adresa, pracovní zařazení, kontakty - prostě cokoliv. To je čistě na vás. Pro bližší seznámení s tím, co Jet Auth požaduje doporučuji seznámit se s příslušným rozhraním.

Role a oprávnění

Pro role platí to samé co pro uživatele. Tedy role musí splňovat minimálně to co požaduje příslušné rozhraní Jet\Auth_Role_Interface. Pokud si k roli přidáte například další interní poznámky, ikony, či cokoliv dalšího co potřebujete, pak je to naprosto v pořádku a má to tak být.

Ovšem v kontextu rolí je dobré vysvětlit si jak funguje systém oprávnění.

Funkce oprávnění

Jak již bylo řečeno, tak pod roli spadá to, čemu se říká oprávněni (privilege) které reprezentuje rozhraní Jet\Auth_Role_Privilege_Interface. Oprávnění je něco, co si můžete a máte definovat sami, dle potřeb vaší aplikace. I když Jet dvě již vestavěná oprávnění má a to pro návštěvu stránky a provedení akce aplikačního modulu - ovšem ostatní oprávnění jsou čistě na vás. Oprávnění je identifikováno textovým řetězcem.

Pojďme si to ukázat rovnou na příkladu. Dejme tomu, že tvoříte firemní intranet a budete potřebovat řídit kdo smí upravovat jaké interní firemní články. Řeknete si tedy, že vytvoříte oprávnění 'edit-intranet-article'. Máme tedy oprávnění, jehož existence opravňuje k něčemu - provedení nějaké operace celkově. Ovšem u intranetu se dá předpokládat, že ne každý bude moci upravovat vše, tedy že bude omezený okruh článků, které daná role může upravovat. To že existuje oprávnění 'edit-intranet-article' samo o sobě nestačí. Toto oprávnění se bude něčeho týkat. Proto se oprávnění skládá ze dvou částí. První částí je název oprávnění, který již v našem příkladu máme definován ('edit-intranet-article'). Druhou částí oprávnění je hodnota (value). A právě hodnota oprávnění konkretizuje. Jedno oprávnění má jeden název, ale má libovolný počet hodnot. Hodnota je ve skutečnosti pole povolených hodnot.

Tedy role má oprávnění 'edit-intranet-article' a toto oprávnění má X hodnot. V našem příkladu budou tyto hodnoty ID článků, které na intranetu jsou.

A pojďme dále teorii přiblížit k praxi. Pochopitelně v systému může být libovolný počet rolí. Každá role je opět identifikována textovým řetězcem (kvůli přehlednosti a použitelnosti). Náš hypotetický intranet má krom řady dalších rolí třeba i role 'editor-HR' a 'editor-accountant'. A obě tyto role mohou mít oprávnění 'edit-intranet-article'. Stejně jako toto oprávnění může (ale nemusí) mít libovolná další role. Oprávnění není tedy pevně vázané na roli, ale naopak je mezi rolemi sdíleno.

Ovšem rozdíl je ten, že role 'editor-HR' má jiné hodnoty oprávnění než role 'editor-accountant'. Tedy role 'editor-HR' pomocí oprávnění 'edit-intranet-article' umožňuje editovat článku 1,8 a 9, zatím co role 'editor-accountant' umožňuje editovat články 2,3 a 11, jejichž ID jsou hodnotami daného oprávnění. Tedy obě role sice umožňují editovat články, ale každá úplně jiné (nebo třeba i společné a totožné - to je samozřejmě také možné). Vše máte v administraci vaší aplikace pořešeno a lze to naklikat Jak konkrétně je na vás - to Jet nediktuje, pouze poskytuje ukázku a inspiraci v ukázkové aplikace.

Teď se od rolí a oprávnění vraťme k uživatelům. Každý uživatel může být v libovolném počtu rolí. Tedy hypotetický uživatel MarieM je v roli editor-HR, uživatel JanH je v roli editor-accountant. To znamená, že MarieM může editovat články 1,8 s 9 a uživatel JanH může editovat články 2,3 a 11. Ovšem pak v systému může existovat uživatel, kterého si nazvěme třeba VelkySef. A tento uživatel má rolí víc. Krom dalších rolí má role 'editor-HR' i 'editor-accountant'. To znamená, že může editovat články 1,2,3,8,9 a 11. Oprávnění se tedy postupně sčítá a navyšuje.

Třídy a moduly v ukázkové aplikaci

Součástí ukázkové aplikace se kterou je Jet distribuován je několik ukázkových implementací potřebných tříd a dokonce i administračních modulů. Tyto třídy a moduly nejsou zamýšleny jako definitivní. Je to pouze ukázka a třeba i základ, který můžete samozřejmě použít dle potřeby..

Kontrolery

  • JetApplication\Auth_Controller_Admin
    Kontroler určený pro řízení přístupu do administrace. Jsou implementované všechny funkce v plném rozsahu.
  • JetApplication\Auth_Controller_Web
    Kontroler pro řízení přístupů do "zaheslovaných" částí webu. Zcela záměrně neumožňuje ověřovat akce modulů (metoda checkModuleActionAccess vždy vrátí false). Je to ukázka toho, že implementace kontroleru by měla plně reflektovat své určení.
  • JetApplication\Auth_Controller_REST
    Kontroler specifický pro REST API server. Opět má několik specifik. Pro přihlášení uživatele vůbec nepoužívá session, ale vždy ověřuje uživatele na základě HTTP autentizace. Tedy používá zcela odlišný postup než předchozí dva kontrolery.

Uživatelé

  • JetApplication\Auth_Administrator_User
    Třída představuje uživatele administrace. Uživatelé jsou konvenčně ukládáni do databáze a jedná se tedy o entitu ORM DataModel.
  • JetApplication\Auth_Visitor_User
    Třída představující návštěvníka webu, který je zaregistrován a může se dostat do neveřejných částí / sekcí. Opět konvenční ukládání do databáze pomocí ORM.
  • JetApplication\Auth_RESTClient_User
    Tato třída představuje klientský uživatelský účet REST API serveru. I zde se jedná o konvenční ukládání do databáze pomocí ORM.

Role a oprávnění

  • JetApplication\Auth_Administrator_Role
    JetApplication\Auth_Administrator_Role_Privilege
    Třídy představují roli a práva uživatele administrace. I role jsou konvenčně ukládáni do databáze a jedná se tedy o entitu ORM DataModel.
  • JetApplication\Auth_Visitor_Role
    JetApplication\Auth_Visitor_Role_Privilege
    Třídy reprezentující roli a práva návštěvníka webu, který je zaregistrován a může se dostat do neveřejných částí / sekcí. Taktéž konvenční ukládání do databáze s pomocí ORM.
  • JetApplication\Auth_RESTClient_Role
    JetApplication\Auth_RESTClient_Role_Privilege
    Tyto třídy představují role a práva klientského uživatelského účtu REST API serveru. Rovněž se jedná o konvenční ukládání do databáze s pomocí ORM.

Napojení uživatele na role

Samozřejmě je nutné určit jaký uživatel je v jaké roli. K tomu slouží následující třídy. U všech se jedná u běžné databázové tabulky a vše opět využívá DataModel. Žádná třída nemá nic specifického, proto pouze výčet:

  • JetApplication\Auth_Administrator_User_Roles
  • JetApplication\Auth_Visitor_User_Roles
  • JetApplication\Auth_RESTClient_User_Roles

Moduly - přihlášení

Kontroler musí být schopen obsloužit stav, kdy uživatel není přihlášen a vybídnout uživatele k přihlášení. Tedy například zobrazit přihlašovací stránku / formulář. Ale to není zdaleka vše. Pokud je uživatel zablokován, tak opět nutné zobrazit uživateli příslušné informace v přívětivé podobě. A co když uživateli vypršela platnost hesla? Nebo co takhle udělat dvoufaktorové přihlášení? A co nějaké řešení zapomenutého hesla?

Zkrátka těch situací co mohou nastat je opravdu hodně. A je na kontroleru toto řešit. Ovšem právě z důvodu velice rozsáhlé logiky toto vše v ukázkové aplikaci neřeší samotné třídy kontrolerů, ale naopak předávají zjištěnou situaci k dořešení aplikačním modulům. Tedy jednoduše řečeno kontrolery zjistí co se děje a co je třeba udělat a pak nechají aplikační modul, ať to v režimu MVC provede. Jak přesně? Koukněte se do metod handleLogin() jednoho z kontrolerů (třídy JetApplication\Auth_Controller_Admin, nebo JetApplication\Auth_Controller_Web).

A jaké ukázkové aplikační moduly řeší přihlašování a řadu dalších souvisejících operací v ukázkové aplikaci? Jsou to:

  • Admin.Login
  • Web.Visitor.Login

Moduly - správa rolí a uživatelů

V praxi je pochopitelně nutné uživatele a jejich role spravovat (přidávat, rušit, nastavovat ...). I ukázku řešení takových věcí najdete v ukázkové aplikaci. Tedy v administraci najdete aplikační moduly pro správu všech tří typů uživatelů i rolí, které jsou součástí ukázkové aplikace. Jsou to tyto moduly:

  • Admin.ManageAccess.Administrators.Roles
  • Admin.ManageAccess.Administrators.Users
  • Admin.ManageAccess.Visitors.Roles
  • Admin.ManageAccess.Visitors.Users
  • Admin.ManageAccess.RESTClients.Roles
  • Admin.ManageAccess.RESTClients.Users

Všechny moduly si můžete nejen vyzkoušet a vše si na nich otestovat, ale můžete je použít jako základ pro vaše řešení.

Předchozí kapitola
Jet\Translator_Dictionary_Phrase
Další kapitola
Jet\Auth_Controller_Interface