Dziś jest poniedziałek, 13 października 2008 roku (z kalendarza...)

Zarządzanie uprawnieniami

Icon

13.05.2007, 16:01

PHP

Komentarze (1)

Powrót

Ostatnio po dłuższej przerwie powróciłem do projektu nowego silnika opartego o Zend Framework. Posprzątawszy kod i zaktualizowawszy wersję biblioteki, zabrałem się za programowanie systemu uprawnień. Jednak po zapoznaniu się z możliwościami pakietu Zend_Acl stwierdziłem, że jest on nieco przerośnięty, jak na potrzeby typowego projektu, a ponadto i tak trzeba do niego dodać własny kod, żeby skądś listę uprawnień pobierać.

Pakiet w teorii zakłada, że programista wczytuje do pamięci skryptu PHP wszystkie reguły uprawnień dla wszystkich grup, jakie tylko występują w aplikacji i dla wszystkich definiuje zasady dostępu do określonych zasobów. O ile w przypadku niewielkiego lub banalnie prostego mechanizmu nie jest to szczególnie wielki problem, to przy pobieraniu odpowiednich danych z bazy przeważnie pobiera się tylko niewielki wycinek listy uprawnień, najczęściej dotyczący aktualnie zalogowanego użytkownika lub grupy w przypadku wizyt anonimowych. W efekcie, korzystając ze standardowego kodu ZF-a, musimy używać bardziej rozbudowanego API oraz z bardziej wymagających algorytmów sprawdzających.

Napisanie własnej obsługi ACL odpowiednio przykrojonej na potrzeby optymalnego wykorzystania bazy danych nie okazało się dużym problemem (hehe, wiem - niedługo z Zend Frameworka w moim silniku nie zostanie nic, poza ideą, ale trudno :D). Należało jednak samodzielnie zaprogramować obsługę drzewiastej struktury listy uprawnień, najlepiej na pojedynczej tablicy, aby uniknąć zabawy z referencjami i wielowymiarowymi potworkami.

<?php
 
	public function isAllowed($rule)
	{
		switch($this -> state)
		{
			case ACL_CHECK:
				$items = explode('/', $rule);
				$id = 0;
				$x = NULL;
				$i = 0;
				$cnt = sizeof($items);
				foreach($items as $item)
				{
					if(is_null($x = $this -> findNode($id, $item)))
					{
						return false;
					}
					elseif($i == $cnt - 1)
					{
						return $this -> rules[$x][1];
					}
					$id = $x;
					$i++;
				}
				return false;
			case ACL_ALLOW_ALL:
				return true;
			case ACL_DENY_ALL:
				return false;
		}
	} // end isAllowed();
 
	public function insertRule($resource, $state)
	{
		$items = explode('/', $resource);
			
		$id = 0;
		$x = NULL;
		$i = 0;
		$cnt = sizeof($items);
		foreach($items as $item)
		{
			if(is_null($x = $this -> findNode($id, $item)))
			{
				$x = $this -> insertNode($id, $item, $i != $cnt - 1 ? false : $state);
			}
			elseif($i == $cnt - 1)
			{
				$this -> rules[$x][1] = $state;
			}
			$id = $x;
			$i++;
		}
	} // end insertRule();
 
	private function insertNode($parent, $name, $state)
	{
		$this -> rules[$this->size] = array(
			0 => $name,		// Name
			1 => $state,	// State
			2 => $parent,	// Parent
			3 => -1,		// Next
			4 => -1,		// First Child
			5 => -1			// Last Child
		);
 
		if($parent >= 0)
		{
			if($this -> rules[$parent][5] != -1)
			{
				$this -> rules[$this->rules[$parent][5]][3] = $this->size;
				$this->rules[$parent][5] = $this -> size;
			}
			else
			{
				$this->rules[$parent][5] = this->rules[$parent][4] = $this -> size;
			}
		}
		$this -> size++;
		return $this -> size - 1;
	} // end insertNode();
 
	private function findNode($parent, $name)
	{
		if(isset($this -> rules[$parent]))
		{
			$id = $this -> rules[$parent][4];
			while($id != -1)
			{
				if($this -> rules[$id][0] == $name)
				{
					return $id;
				}
				$id = $this -> rules[$id][3];
			}				
		}
		return NULL;	
	} // end findNode();
?>

Nowy ACL nie jest przystosowany do jednej, konkretnej reprezentacji uprawnień w bazie danych. W przyszłych projektach budowanych na bazie tego silnika na pewno pojawią się różne specyficzne wymagania, których nie będzie dało się uwzględnić w pojedynczej, uniwersalnej tabeli uprawnień. Dlatego do kompletu dorzuciłem interfejs ładowarki uprawnień, którego implementacja da nam obsługę pojedynczego źródła listy uprawnień. W trakcie przetwarzania skryptu uprawnienia można ładować z kilku takich źródeł, po prostu przekazując odpowiednie obiekty w miarę potrzeb:

$acl = new Terenzzia_Acl;
$generalAclSource = new Terenzzia_Acl_General;
$generalAclSource -> setOptions($session -> get('Auth', 'userId'), $config -> terenzzia -> primaryRole);
$acl -> addRules($generalAclSource);

Aktualnie brakuje jeszcze dziedziczenia uprawnień po innej grupie i jeszcze się nie zastanawiałem, w jaki sposób to zrealizować. Są dwie możliwości:

  1. Dziedziczenie statyczne - w momencie tworzenia w panelu administracyjnym nowej grupy i ustawieniu dziedziczenia, w bazie danych powstają kopie rekordów. Zalety: prostsze zapytania :). Wady: zmiana uprawnień w grupie nadrzędnej nie jest widoczna w grupach potomnych.
  2. Dziedziczenie dynamiczne - w bazie danych zapisane są tylko informacje, która grupa po której dziedziczy i mechanizm ACL na ich podstawie pobiera odpowiednie uprawnienia. Tu z kolei mamy przeciwieństwo, tzn. większe możliwości za cenę wydajności i stopnia komplikacji problemu.

Trzeba się teraz zastanowić, na czym mi bardziej zależy, ew. co mi się bardziej chce zaprogramować :).

Powrót

Komentarze

Napisał MiB w niedzielę, 13 maja 2007 o 17:20

Witam

Implementacja uprawnień grupowych zależy również od potrzeb. W mniejszym serwisie (o mniejszej ilości przewidywanych odwiedzin) można sobie pozwolić na metodę drugą. W większym - na pierwszą.
Gdyby metodą pierwszą wzbogacić o aktualizowanie potomków w momencie edycji przodka, byłoby całkiem sympatycznie :)
(a aby nie napisać się zbyt wiele, można po prostu czyścić tabele uprawnień i wpisywać na nowo - przecież zmiany w uprawnieniach nie będą następować co 10 minut...).

Pozdrawiam

Strona 1 z 1 :: 1

Skomentuj

NickInformacja
E-mailTylko do użytku wewnętrznego.
WWWNie zapomnij o http://
LayoutNapisz tu, czy widzisz dzienny czy nocny layout.
WpisFormatowanie wiki
Internauto, pamiętaj! Wolność to nie samowola - dbaj o kulturę wypowiedzi oraz dyskusji w sieci.

Na Zyxist.com panuje swoboda wyrażania opinii oraz krytyki pod dowolnym adresem. Jedyny warunek: musi być ona kulturalna i rzeczowa. Na chamstwo, prostactwo lub jawne obrażanie kogokolwiek nie ma tu miejsca i takie komentarze są bardzo szybko usuwane. Jeśli zamierzasz polemizować z treścią wpisu, wpierw uważnie ją przeczytaj.

© Tomasz "Zyx" Jędrzejewski 2005 - 2008 | Wykonanych zapytań: 2 | Serwer wirtualny zapewnia