Amelia posiada wbudowany mechanizm ochrony/kontroli względem roli dla definiowanych akcji. Jest on zintegrowany z mechanizmem sesji (role dodane użytkownikowi zapisywane są w sesji). Programista decyduje jakiej roli wymagają akcje podczas definiowania routingu oraz dodaje użytkownikowi role w momencie jego zalogowania do systemu.

Weźmy przykładową zawartość pliku routing.php projektu startowego w Amelii (pomijając linie zakomentowane):

 

use core\App;
use core\Utils;

App::getRouter()->setDefaultRoute('hello'); #default action

Utils::addRoute('hello', 'HelloCtrl');


zawiera on definicję jednej akcji hello skierowanej do obsługi na kontroler HelloCtrl. Dodatkowo, akcja ta jest skonfigurowana jako domyślna (jeśli akcja nie zostanie podana lub nie będzie istnieć to zostanie uruchomiona akcja hello).

Utils::addRoute('hello', 'HelloCtrl') dodaje do routingu akcję jako publiczną, to znaczy taką, którą można uruchomić zawsze, bez konieczności logowania do systemu (posiadania szczególnej roli). Aby zdefiniować akcję z wymogiem posiadania roli należy je wprowadzić jako trzeci parametr w formie tablicy.

Utils::addRoute('pokaz_dane', 'DaneCtrl', ["user"]) dodaje zatem do routingu nową akcję, która wymaga posiadania roli user. Bez zalogowania jej wykonanie nie będzie możliwe. Aby tego dokonać należy przeprowadzić autoryzację użytkownika, czyli posiadać formularz logowania oraz kontroler, który wykona to zadanie. Po poprawnym procesie logowania dana rola, np. user, może zostać użytkownikowi dodana i dopóki się nie wyloguje lub sesja nie wygaśnie, będzie mógł wywołać chronione akcje.

Wyobraźmy sobie zatem bardziej kompletny routing, zawierający akcje logowania, wylogowania oraz inofrmacji o braku uprawnień:

 

App::getRouter()->setDefaultRoute('hello'); #default action
App::getRouter()->setLoginRoute('accessdenied'); #action to forward if no permissions

Utils::addRoute('login', 'LoginCtrl');
Utils::addRoute('logout', 'LoginCtrl');

Utils::addRoute('hello', 'HelloCtrl');
Utils::addRoute('accessdenied', 'HelloCtrl');

Utils::addRoute('showdata', 'DataCtrl',["user","admin"]);
Utils::addRoute('cleardata', 'DataCtrl',["admin"]);

 

Powyższy routing zawiera w definicjach akcji już trzy kontrolery. LoginCtrl odpowiada za logowanie i wylogowanie użytkownika. Akcje login i logout są publiczne (szczególnie login musi być publiczna). HelloCtrl zawiera dwie publiczne akcje. Należy zwrócić uwagę na drugą, nową akcję accessdenied, która odpowiada za wyświetlenie widoku z informacją o braku uprawnień. Ta akcja na początku jest wybrana jako ta do której należy się przekierować gdy ktoś nie ma uprawnień (spróbuje wywołać chronioną akcję kiedy nie jest zalogowany).

Ostatnim kontrolerem jest DataCtrl, który obsługuje akcje chronione. Akcja showdata wymaga do wywołania roli user lub admin, natomiast akcja cleardata wymaga roli admin.

Zapewnieniem ochrony zajmuje się obiekt routera przy wywołaniu akcji. Jednak programista nie musi się przejmować sposobem jej realizacji, a jedynie skupić się na odpowiedniej definicji routingu.

Zalogowanie użytkownika

Routing zdefiniowany. Przyjrzyjmy się zatem bliżej zadaniom kontrolera logowania, ponieważ to od niego zależy proces dodawania roli użytkownikowi.

LoginCtrl obsługuje w przykładzie dwie akcje: login oraz logout. Pierwsza akcja powinna wyświetlić formularz logowania, gdy nie zostaną przesłane parametry logowania lub gdy są niepoprawne. Gdy natomiast są poprawne powinna dodać użytkownikowi odpowiednią rolę.

Rola jest najczęściej przechowywana w bazie danych, razem z innymi informacjami o użytkowniku. Zatem proces walidacji w obsłudze akcji login powinien:

  1. spróbować pobrać dane użytkownika z bazy na podstawie podanego loginu i hasła
  2. a gdy proces się powiedzie (otrzymamy rekord z danymi) oznacza to, że użytkownik istnieje i dane logowania podane są poprawnie. Wśród wyników będzie rola, którą można dodać użytkownikowi w systemie (zapisać w sesji) za pomocą metody \core\Roleutils::addRole().

Przedstawmy zatem najważniejsze fragmenty kodu akcji dla logowania:

public function action_login() {

//1. pobranie parametrów formularza logowania (login i hasło)
...

//2. walidacja (pobranie z BD informacji o użytkowniku)
... //załóżmy, że rola użytkownika zostanie tu zapisana w zmiennej $rola

//3.1 jeśli walidacja poprawna to "zaloguj"
\core\RoleUtils::addRole($rola); //zapisanie roli w sesji

// i przekieruj do wybranej akcji (tej domyślnej po zalogowaniu)
App::getRouter()->redirectTo("showdata");

//3.2 jeśli walidacja niepoprawna to pozostań na stronie logowania i wyświetl komunikaty
...

}

 

Jak widać na powyższym zrzucie, jedynym wymogiem w celu dodania użytkownikowi roli w Amelii jest wywołanie metody addRole() klasy RoleUtils. Z reguły po poprawnym zalogowaniu przekierowujemy na jedną z akcji, która wymagała ochrony. Jeśli natomiast logowanie jest nieudane (nieudana walidacja) to pozostań na stronie logowania (wyświetl ponownie odpowiedni widok).

Należy tu podkreślić, iż niejednokrotnie oprócz dodania roli chcemy zapisać w sesji jeszcze inne informacje jak np: nazwa użytkownika, jego id z bazy danych (na cele wstawiania rekordów z nim powiązanych w innych tabelach) i inne. Można to zrobić używając już standardowej struktury $_SESSION - do zapisu i odczytu w późniejszym etapie w innych kontrolerach. Warto również wspomnieć o usprawnieniu tego procesu w Amelii za pomocą klasy \core\SessionUtils. Zawiera ona metody pozwalające na proste zapisywanie w sesji obiektów i bardziej złożonych struktur.

Wylogowanie użytkownika

Proces wylogowania jest już bardzo prosty. Bez zbędnych komentarzy można przedstawić przykładową metodę akcji wykonującą ten proces:

public function action_logout() {

//unieważnij sesję
session_destroy();

// i przekieruj do wybranej akcji (tej domyślnej po wylogowaniu)
App::getRouter()->redirectTo("hello");

}