Re: Poeksperymentujmy z kontrolą uprawnień

Zyx ma ciekawe przemyślenia, ja również je mam (tak mi się zdaje) więc tym razem moje RE, będzie dotyczyć jego artykułu pt. poeksperymentujmy z kontrolą uprawnień.

Przedstawił on w swoim artykule sposób na weryfikacje czy dany kod ma dostęp do innego kodu na podstawie domen. Domena była obszarem gdzie obowiązywała dana polityka bezpieczeństwa, jeżeli zewnętrzny kod chciał na niej wykonać jakąś operacje, musiał wywołać metodę safeOperation z argumentami charakterystycznymi dla danej operacji i podać domenę z której pochodzi. Metoda safeOperation weryfikowała za pomocą policyManagera czy kod z innej domeny ma prawo wywołać daną operację i jeżeli ich nie posiadał to wyrzucany był wyjątek.

Rozwiązanie, które tu przedstawię jest w pewnym sensie generalizacją koncepcji Zyx-a. Na czym ono polega ?

Pierwsze założenie jest takie, że wszystkimi obiektami (a przynajmniej tymi które chcemy zabezpieczyć) zarządzą kontener IoC (inversion of control). Jest to maszynka, która tworzy nam i konfiguruje obiekty. Mój kontener IoC ma tą ciekawą właściwość, że pozwala zwracać zamiast żądanego obiektu jego proxy. Proxy jest obiektem przez którego przelatują wszystkie żądania do docelowego obiektu i posłuży on do kontroli uprawnień.

Cała idea leży w tym, że proxy przy próbie wywołania metody na obiekcie docelowym, sprawdzi w Acl, czy aktualny użytkownik (może być cokolwiek innego np wywołujący obiekt) ma uprawnienia do wywołania owej metody i jeżeli nie to rzuci wyjątkiem.

Żeby nie być gołosłownym, przedstawię fragmenty kodu jak to działa, na końcu artykułu będzie też link do kompletnego przykładu razem z moim kontenerem IoC.

Po pierwsze konfiguracja IoC (pełną informacje o konfiguracji znajdziesz w artykule o kontenerze IoC):

<object-config>
    <application>
        <!-- For now empty, objects declared here will be cached by APC or something similiar -->
    </application>
    <session>

    </session>
    <request>   
   
        <object class="Foo1">
            <property name="param2">other_baz</property>
            <property name="param1">baz</property>
            <proxy class="AccessProxy" />
        </object>
        <object class="AccessProxy">
            <property name="acl" type="class">Acl</property>
        </object>
        <object class="Acl">
            <property name="user" type="class">User</property>
        </object>
        
    </request>    
</object-config>

I klasy biorące udział w eksperymencie:
klasa docelowa:

class Foo1 {

    private $param1;
    private $param2;
    

    function __construct($param1, $param2){
        $this->param1 = $param1;
        $this->param2 = $param2;
    }

    function doHello(){
        echo 'hello <br/>';
    }

    function doFoo(){
        echo 'foo <br/>';
    }

}

Klasa Acl:

class Acl {

    protected $_user;
    public function __construct(User $user){
        $this->_user = $user;
    }

    public function isCallable($class, $method){
        
        if($this->_user->id == 10 and $method == 'doHello'){
            return true;
        }
        if($this->_user->id == 15 and $method == 'doFoo'){
            return true;
        }
        return false;
    }
}

Minimalistyczna klasa użytkownika 😉

class User {
    public $id = 10;
}

oraz najważniejsze – klasa proxy:

class AccessProxy implements IContainerProxy{

    protected $_subject;
    protected $_subjectClass;
    protected $_acl;

    public function __construct($subject) {
         
        $this->_subject = $subject;
        $this->_subjectClass = get_class($subject);
    }

    public function setAcl(Acl $acl){
        $this->_acl = $acl;
    }

    public function  __call($name, $arguments) {
       
        if($this->_acl->isCallable($this->_subjectClass, $name)){
            return call_user_func_array(array($this->_subject, $name), $arguments);
        }
        throw new Exception('No access!');
    }
}

Wykorzystanie:

$oManager = new CContainerManager($oConfig, 'sample', 10000);
$oObj = $oManager->factory('Foo1');

$oObj->doHello();
// hello
$oUser = $oManager->factory('User');

$oUser->id = 15;

$oObj->doFoo();
//foo
$oObj->doHello();
// exception: no access !

Dzięki zastosowaniu kontenera IoC i obiektu proxy można zrealizować koncepcje Zyx-a w sposób transparentny dla programisty. Ten przykład nie korzysta z jego kodu, natomiast daje pewien pogląd na sprawę jak by to mogło wyglądać. Btw. podobny przykład, jednak bez użycia kontenera IoC zaprezentowałem we wpisie poświęconemu wzorcowi Proxy – tam zastosowana została fabryka.
Pełny kod przykładu można ściągnąć tutaj

Leave a Reply

Informuj mnie o odpowiedziach poprzez e-mail. Możesz również subskrybować wpis bez zostawiania komentarza.