RBAC skrócenie zapisu uprawnień

Mam sobie kontroler z uprawnieniami w któym mam stworzone wszystkie potrzebne uprawnienia. Dla np 10 kontrolerów zapisów jest:

10x 5(curd + index) uprawnienia + 10 x 5 dodania obiektu + tyle samo polaczenie Roli z uprawnieniem, sporo tego…




 public function actionInit()

    {

        $auth = Yii::$app->authManager;

        $auth->removeAll();


        /**

         * Category perrmission

         */

        $Category = $auth->createPermission('category/index');

        $CreateCategory = $auth->createPermission('category/create');

        $updateCategory = $auth->createPermission('category/update');

        $deleteCategory = $auth->createPermission('category/delete');

        $viewCategory = $auth->createPermission('category/view');


  $auth->add($Category);  // czy tego jakoś w pętli nie da się skrócić skoro utworzone są już wcześniej uprawnienia? 

.....






Sprawdzam w ten sposób:


 public function behaviors()

    {

        $behaviors['access'] = [

            'class' => AccessControl::className(),

            'rules' => [

                [

                    'allow'=>true,

                    'roles' => ['Admin'],

                    'matchCallback' => function ($rule, $action) {


                        $action = Yii::$app->controller->action->id;

                        $controller = Yii::$app->controller->id;

                        $route  = "$controller/$action";

                        $post = Yii::$app->request->post();

                        if (\Yii::$app->user->can($route)) {

                            return true;

                        };

                        return false;

                    },

                    'denyCallback' => function ($rule, $action) {

                        Yii::$app->session->setFlash('error', 'This section is only for registered users.');

                    },


                ],

            ],

        ];


        return $behaviors;

    }

Wszystko działa ok, ale martwi mnie trochę powtarzalność tych zapisów. Tak samo tworzenie uprawnień jak metoda Behaviors() która jest kopiowana za kazdym razem.

Da się to jakoś skrócić czy ma być jak jest?

Pytanie czy koniecznie potrzebujesz mieć osobne uprawnienia dla każdej metody? Zwykle wystarczy stworzyć kilka ogólnych.

To zależy. Głownie chodzi mi o to że mając podzieloną aplikację na backend/frontend nie chcę by zwykły użytkownik miał dostęp do backendu. Weźmy przykład tabelę kategorie. Jeżeli założę tylko na index uprawnienia to user może wpisać sobie category/create i uzyska zdaje się normalnie dostęp do tworzenia kategorii.

To wystarczy załatwić rolą typu ‘admin’ i zablokowaniem dostępu dla wszystkich pozostałych. Jeśli jednak upierasz się przy tak wielkiej ilości uprawnień to daj znać, to przejdziemy do zapisu AccessControl.

Co masz na myśli mówiąc: "rolą typu admin"? możesz pokazać przykład? U mnie rola jest powiązana z uprawnieniami.

Nie upieram się, ale chętnie dowiem się jak to wyglądało by inaczej z AC. (U mnie też niby go wykorzystuje)

A tak w ogóle ową metodę sprawdzania znalazłem na internecie gdzieś więc tak zrobiłem ;)

Ok, przy założeniu, że chcemy trzymać userów z frontendu i backendu w tej samej tabeli w bazie, ale tylko wybrani powinni móc odpalić dowolną metodę z backendu:

  1. Tworzysz nową rolę o nazwie np. admin.

  2. Przydzielasz tę rolę do usera.

  3. W kontrolerze backendowym dodajesz behavior z filtrem dostępu:




    public function behaviors()

    {

        return [

            'access' => [

                'class' => AccessControl::className(),

                'rules' => [

                    [

                        'allow' => true,

                        'roles' => ['admin'], // używasz tutaj tej nazwy

                    ],

                ],

            ],

        ];

    }



Ponieważ nie wymieniłeś żadnej akcji, wszystkie są objęte zasadą. Tylko zalogowany user z ustawioną rolą ‘admin’ będzie mógł wywołać akcję kontrolera, wszyscy pozostali zostaną przekierowani do logowania, bądź zobaczą stronę ze statusem 403 zdaje się.

Jak widzisz, nie trzeba dodawać poszczególnych akcji, minimum roboty. Rzecz jasna w momencie, gdy masz różnych administratorów z różnym poziomem uprawnień to już trzeba to trochę skomplikować.

Dzięki, faktycznie to jest dużo prostsze ;)

Jeszcze dwa pytania;

Jak przekierować z czegoś takiego do np strony logowania? bo teraz jest rzucany wyjątek 403 na "surowo".

https://gyazo.com/a8c406d6b2be8bc465b691f8f280eedf ?

Próbowałem:


    public function behaviors()

    {

        return [

            'access' => [

                'class' => AccessControl::className(),

                'rules' => [

                    [

                        'allow' => true,

                        'roles' => ['Admin'], // używasz tutaj tej nazwy


                        'denyCallback' => function ($rule, $action) {

                             if(!$action) return $this->redirected('logowanie ');

                        },

                    ],

                ],

            ],

        ];

    }

2 teraz widzę zasadę działania tego wszystkiego, ale nadale mam wątpliwość. Czy to co pokazałem w pytaniu jest złe? czy te uprawnienia powinny siedzieć w bazie czy lepiej ręcznie w behavior ustawiać akcje do jakich np admin ma dostęp? Jest jakaś różnica?

Wyjątek jest rzucany, bo user jest zalogowany. Jeśli nie będzie zalogowany, to domyślnie przerzuci go do strony logowania.

To co pokazałeś w pytaniu nie jest złe, aczkolwiek można by to pewniej odrobinę krócej zapisać. Poza tym po co tam jest zmienna $post ustawiania?

Jeśli potrzebujesz wyróżnić tylko kilka akcji, które będą chronione, to możesz wymienić je w kluczu ‘actions’ zasady.

Jeśli potrzebujesz jedynie zablokować dostęp do backendu dla innych userów niż admin, można zrobić to bez użycia RBAC - wystarczy dodać osobny komponent do logowania i pobierać model Identity z wyróżnionym atrybutem roli w bazie. Wtedy tylko user z odpowiednią wartością kolumny w bazie będzie mógł zalogować się na backend, zatem w filtrze dostępu wystarczy sprawdzić, czy user jest zalogowany (rola ‘@’).

A jak zamiast tak brzydkiego komunikatu przekierować usera do jakiejś strony? dziwne że danycallback nie działa.

Mógłbyś pokazać jakiś pseudokod albo kod z tym identity? bo nie bardzo rozumiem "osobny komponent do logowania"

Czyli reasumując. Jeżeli tworzę bardziej skomplikowany system z moderatorami, różnymi akcjami itd to lepiej to trzymać w bazie wszystko, a jezeli chcę zmienić uprawnienia kilku dla kilku akcji to ręcznie w behavior to wstawić?

p.s

ten post to nie wiem po co jest. Pewnie jakąs pozostałość po czymś.

To powinno zadziałać (denyCallback wewnątrz rule):




'denyCallback' => function ($rule, $action) {

    return $this->redirect(/* route */);

}



Komponent:

W aplikacji masz model X, który implementuje IdentityInterface. Załóżmy, że dodałeś w tabeli user w bazie kolumnę admin - jeśli jej wartość = 0 to zwykły user, jeśli = 1 to admin i tylko admin ma mieć dostęp do backendu.

Frontend ma model X, na który wskazuje ‘identityClass’ w konfiguracji komponentu user. Tu nie musimy nic zmieniać.

Backend ‘identityClass’ wskazuje na XAdmin, który może być rozszerzeniem X z frontu lub jego kopią - ważne jest, żeby jego metoda findIdentity() miała zawarty warunek w SQL ‘admin’ => 1. Tylko user z admin = 1 będzie mógł się zalogować. W kontrolerach backendowych dodajesz behavior z access filtrem i warunkiem ‘roles’ => [’@’].

Jeśli potrzebujesz mieć różne uprawnienia to implementujesz RBAC, jeśli nie, to nie trzeba się w to bawić.

Dzięki za wyjaśnienie całości ;)