ACL i kontrolery
Po pierwsze: są to najczęściej różne rzeczy / warstwy. Kiedy krytykujesz przykładowy kod kontrolera, łączy on oba razem - najwyraźniej zbyt ciasno.
tereško przedstawił już sposób, w jaki można to bardziej oddzielić od wzoru dekoratora.
Cofnąłbym się najpierw o krok wstecz, aby znaleźć pierwotny problem, z którym się borykasz, i trochę go przedyskutować.
Z jednej strony chcesz mieć kontrolery, które po prostu wykonują pracę, do której są polecane (polecenie lub akcja, nazwijmy to polecenie).
Z drugiej strony chcesz mieć możliwość umieszczenia listy ACL w swojej aplikacji. Przedmiotem tych list ACL powinno być - jeśli dobrze zrozumiałem twoje pytanie - kontrolowanie dostępu do niektórych poleceń twoich aplikacji.
Ten rodzaj kontroli dostępu wymaga zatem czegoś innego, co łączy te dwa elementy. Na podstawie kontekstu, w którym polecenie jest wykonywane, rozpoczyna się lista ACL i należy podjąć decyzję, czy określone polecenie może zostać wykonane przez określony podmiot (np. Użytkownika).
Podsumujmy w tym miejscu, co mamy:
Komponent ACL jest tutaj centralny: musi wiedzieć przynajmniej coś o poleceniu (aby precyzyjnie zidentyfikować polecenie) i musi być w stanie zidentyfikować użytkownika. Użytkownicy są zwykle łatwo identyfikowani za pomocą unikalnego identyfikatora. Ale często w aplikacjach internetowych są użytkownicy, którzy nie są w ogóle identyfikowani, często nazywani gośćmi, anonimowymi, wszyscy itd. W tym przykładzie zakładamy, że lista ACL może zużywać obiekt użytkownika i hermetyzować te szczegóły. Obiekt użytkownika jest powiązany z obiektem żądania aplikacji i lista ACL może go używać.
A co z identyfikacją polecenia? Twoja interpretacja wzorca MVC sugeruje, że polecenie jest złożone z nazwy klasy i nazwy metody. Jeśli przyjrzymy się bliżej, dla polecenia są nawet argumenty (parametry). Więc ważne jest, aby zapytać, co dokładnie identyfikuje polecenie? Nazwa klasy, nazwa metody, liczba lub nazwy argumentów, a nawet dane zawarte w którymkolwiek z argumentów lub połączenie tego wszystkiego?
W zależności od poziomu szczegółowości potrzebnego do zidentyfikowania polecenia w liście ACL, może się to znacznie różnić. Na przykład zachowajmy to po prostu i określmy, że polecenie jest identyfikowane przez nazwę klasy i nazwę metody.
Zatem kontekst tego, w jaki sposób te trzy części (ACL, Polecenie i Użytkownik) należą do siebie nawzajem, jest teraz bardziej jasny.
Można powiedzieć, że z wyimaginowanym komponentem ACL możemy już wykonać następujące czynności:
$acl->commandAllowedForUser($command, $user);
Zobacz tylko, co się dzieje: dzięki umożliwieniu identyfikacji zarówno polecenia, jak i użytkownika, lista ACL może wykonać swoją pracę. Zadanie listy ACL nie jest związane z pracą obiektu użytkownika i konkretną komendą.
Brakuje tylko jednej części, to nie może żyć w powietrzu. I tak nie jest. Musisz więc zlokalizować miejsce, w którym musi zostać uruchomiona kontrola dostępu. Przyjrzyjmy się, co dzieje się w standardowej aplikacji internetowej:
User -> Browser -> Request (HTTP)
-> Request (Command) -> Action (Command) -> Response (Command)
-> Response(HTTP) -> Browser -> User
Wiemy, że aby zlokalizować to miejsce, musi ono nastąpić przed wykonaniem konkretnego polecenia, więc możemy zredukować tę listę i wystarczy spojrzeć na następujące (potencjalne) miejsca:
User -> Browser -> Request (HTTP)
-> Request (Command)
W pewnym momencie aplikacji wiesz, że określony użytkownik zażądał wykonania konkretnego polecenia. Wykonujesz już tutaj pewnego rodzaju listę ACL: Jeśli użytkownik zażąda polecenia, które nie istnieje, nie zezwalasz na jego wykonanie. Zatem gdziekolwiek zdarzy się to w Twojej aplikacji, może być dobrym miejscem na dodanie „prawdziwych” kontroli ACL:
Polecenie zostało zlokalizowane i możemy go zidentyfikować, aby lista ACL mogła sobie z nim poradzić. W przypadku, gdy polecenie nie jest dozwolone dla użytkownika, polecenie nie zostanie wykonane (akcja). Może CommandNotAllowedResponse
zamiast CommandNotFoundResponse
dla przypadku, żądanie nie może zostać przekształcone w konkretne polecenie.
Często nazywane jest miejsce, w którym mapowanie konkretnego żądania HTTPRequest jest mapowane na polecenie routingiem . Ponieważ Routing ma już zadanie zlokalizowania polecenia, dlaczego nie rozszerzyć go, aby sprawdzić, czy polecenie jest rzeczywiście dozwolone na liście ACL? Na przykład poprzez rozszerzenie Router
do ACL świadomy routera: RouterACL
. Jeśli twój router jeszcze nie zna User
, Router
to nie jest to właściwe miejsce, ponieważ aby ACL działało nie tylko polecenie, ale także użytkownik musi być zidentyfikowany. Więc to miejsce może się różnić, ale jestem pewien, że możesz łatwo zlokalizować miejsce, które chcesz rozszerzyć, ponieważ jest to miejsce, które spełnia wymagania użytkownika i polecenia:
User -> Browser -> Request (HTTP)
-> Request (Command)
Użytkownik jest dostępny od początku, najpierw polecenie z Request(Command)
.
Więc zamiast umieszczać kontrole ACL wewnątrz konkretnej implementacji każdego polecenia, umieszczasz je przed nim. Nie potrzebujesz żadnych ciężkich wzorców, magii lub czegokolwiek, ACL wykonuje swoją pracę, użytkownik wykonuje swoją pracę, a zwłaszcza polecenie wykonuje swoją pracę: tylko polecenie, nic więcej. Komenda nie ma interesu, aby wiedzieć, czy mają do niej zastosowanie role, czy jest gdzieś strzeżona, czy nie.
Więc po prostu oddzielaj rzeczy, które do siebie nie należą. Użyj nieznacznego przeformułowania zasady pojedynczej odpowiedzialności (SRP) : Powinien być tylko jeden powód do zmiany polecenia - ponieważ polecenie się zmieniło. Nie dlatego, że teraz wprowadzasz ACL do swojej aplikacji. Nie dlatego, że zmienisz obiekt użytkownika. Nie dlatego, że przeprowadzasz migrację z interfejsu HTTP / HTML do interfejsu SOAP lub wiersza poleceń.
ACL w twoim przypadku kontroluje dostęp do polecenia, a nie samo polecenie.
if($user->hasFriend($other_user) || $other_user->profileIsPublic()) $other_user->renderProfile()
(innego, wyświetlacz „Nie masz dostępu do profilu tego użytkownika” lub coś podobnego, że nie rozumiem?.