Bezpieczeństwo bazy danych jest ważne dla każdej konfiguracji MySQL. Użytkownicy są podstawą każdego systemu. Jeśli chodzi o systemy baz danych, ogólnie myślę o nich w dwóch odrębnych grupach:
- Użytkownicy aplikacji, usług lub programów — głównie klienci lub klienci korzystający z usługi.
- Twórcy baz danych, administratorzy, analitycy itp… - Osoby utrzymujące, pracujące z lub monitorujące infrastrukturę bazy danych.
Chociaż każdy użytkownik musi uzyskać dostęp do bazy danych na pewnym poziomie, te uprawnienia nie są jednakowe.
Na przykład klienci i klienci potrzebują dostępu do danych „powiązanych kont użytkowników”, ale nawet to powinno być monitorowane z pewnym poziomem kontroli. Jednak niektóre tabele i dane powinny być całkowicie niedostępne (np. tabele systemowe).
Niemniej jednak:
- Analityk potrzebuje „dostępu do odczytu ', aby zebrać informacje i wgląd za pomocą tabel zapytań…
- Programiści potrzebują wielu uprawnień i uprawnień, aby wykonywać swoją pracę…
- DBA potrzebuje uprawnień „root” lub podobnego typu, aby uruchomić program…
- Kupujący usługę muszą zobaczyć swoją historię zamówień i płatności…
Możesz sobie wyobrazić (wiem, że wiem), jak trudne jest zadanie zarządzania wieloma użytkownikami lub grupami użytkowników w ekosystemie bazy danych.
W starszych wersjach MySQL środowisko wielu użytkowników jest ustanawiane w dość monotonny i powtarzalny sposób.
Jednak wersja 8 implementuje wyjątkową i potężną standardową funkcję SQL – „Role” – która łagodzi jeden z bardziej nadmiarowych obszarów całego procesu:przypisywanie uprawnień użytkownikowi.
Jaka jest rola w MySQL?
Na pewno możesz odwiedzić MySQL w 2018 r.:Co jest w 8.0 i inne obserwacje, napisałem na blogu Manynines tutaj, w którym wspominam o rolach, aby uzyskać ogólny przegląd. Jednak tam, gdzie tylko je podsumowałem, ten obecny post ma zagłębić się i skupiać się wyłącznie na rolach.
Oto jak dokumentacja online MySQL definiuje rolę:„Rola MySQL to nazwany zbiór uprawnień”.
Czy sama ta definicja nie wydaje się pomocna?
Ale jak?
Zobaczymy w poniższych przykładach.
Aby zanotować podane przykłady
Przykłady zawarte w tym poście dotyczą rozwoju osobistego „jednego użytkownika” i uczenia się stacji roboczej/środowiska, więc upewnij się, że wdrażasz najlepsze praktyki, które przynoszą korzyści dla Twoich konkretnych potrzeb lub wymagań. Zademonstrowane nazwy użytkowników i hasła są całkowicie arbitralne i słabe.
Użytkownicy i uprawnienia w poprzednich wersjach
W MySQL 5.7 role nie istnieją. Nadawanie uprawnień użytkownikom odbywa się indywidualnie. Aby lepiej zrozumieć, jakie role zapewniają, nie używajmy ich. Wiem, że to w ogóle nie ma sensu. Ale w miarę postępów w poście tak się stanie.
Poniżej tworzymy kilku użytkowników:
CREATE USER 'reader_1'@'localhost' IDENTIFIED BY 'some_password';
CREATE USER 'reader_writer'@'localhost' IDENTIFIED BY 'another_password';
CREATE USER 'changer_1'@'localhost' IDENTIFIED BY 'a_password';
Następnie ci użytkownicy otrzymują pewne uprawnienia:
GRANT SELECT ON some_db.specific_table TO 'reader_1'@'localhost';
GRANT SELECT, INSERT ON some_db.specific_table TO 'reader_writer'@'localhost';
GRANT UPDATE, DELETE ON some_db.specific_table TO 'changer_1'@'localhost';
Uff, cieszę się, że to już koniec. Teraz wróć do…
I tak po prostu masz prośbę o zaimplementowanie dwóch kolejnych użytkowników „tylko do odczytu”…
Wróć do deski kreślarskiej:
CREATE USER 'reader_2'@'localhost' IDENTIFIED BY 'password_2';
CREATE USER 'reader_3'@'localhost' IDENTIFIED BY 'password_3';
Przypisywanie im również uprawnień:
GRANT SELECT ON some_db.specific_table TO 'reader_2'@'localhost';
GRANT ALL ON some_db.specific_table TO 'reader_3'@'localhost';
Czy widzisz, że jest to mniej produktywne, pełne powtórzeń i podatne na błędy? Ale co ważniejsze, czy złapałeś błąd?
Dobrze dla Ciebie!
Przyznając uprawnienia tym dwóm dodatkowym użytkownikom, przypadkowo przyznano WSZYSTKIE uprawnienia nowemu użytkownikowi reader_3.
Ups.
Błąd, który każdy może popełnić.
Wprowadź role MySQL
W przypadku ról wiele z powyższych systematycznych przypisywanie i delegowanie uprawnień może być nieco uproszczone .
Tworzenie użytkownika zasadniczo pozostaje takie samo, ale przypisuje uprawnienia poprzez różne role:
mysql> CREATE USER 'reader_1'@'localhost' IDENTIFIED BY 'some_password';
Query OK, 0 rows affected (0.19 sec)
mysql> CREATE USER 'reader_writer'@'localhost' IDENTIFIED BY 'another_password';
Query OK, 0 rows affected (0.22 sec)
mysql> CREATE USER 'changer_1'@'localhost' IDENTIFIED BY 'a_password';
Query OK, 0 rows affected (0.08 sec)
mysql> CREATE USER 'reader_2'@'localhost' IDENTIFIED BY 'password_2';
Query OK, 0 rows affected (0.28 sec)
mysql> CREATE USER 'reader_3'@'localhost' IDENTIFIED BY 'password_3';
Query OK, 0 rows affected (0.12 sec)
Pytając o tabelę systemową mysql.user, możesz zobaczyć, że istnieją nowo utworzeni użytkownicy:
(Uwaga:mam kilka kont użytkowników w tym środowisku uczenia się / rozwoju i stłumiłem większość danych wyjściowych, aby uzyskać lepszą przejrzystość na ekranie.)
mysql> SELECT User FROM mysql.user;
+------------------+
| User |
+------------------+
| changer_1 |
| mysql.infoschema |
| mysql.session |
| mysql.sys |
| reader_1 |
| reader_2 |
| reader_3 |
| reader_writer |
| root |
| | --multiple rows remaining here...
+------------------+
23 rows in set (0.00 sec)
Mam tę dowolną tabelę i przykładowe dane:
mysql> SELECT * FROM name;
+--------+------------+
| f_name | l_name |
+--------+------------+
| Jim | Dandy |
| Johhny | Applesauce |
| Ashley | Zerro |
| Ashton | Zerra |
| Ashmon | Zerro |
+--------+------------+
5 rows in set (0.00 sec)
Użyjmy teraz ról do ustanowienia i przypisania uprawnień nowym użytkownikom do korzystania z tabeli nazw.
Najpierw utwórz role:
mysql> CREATE ROLE main_read_only;
Query OK, 0 rows affected (0.11 sec)
mysql> CREATE ROLE main_read_write;
Query OK, 0 rows affected (0.11 sec)
mysql> CREATE ROLE main_changer;
Query OK, 0 rows affected (0.14 sec)
Zwróć uwagę na tabelę mysql.user ponownie:
mysql> SELECT User FROM mysql.user;
+------------------+
| User |
+------------------+
| main_changer |
| main_read_only |
| main_read_write |
| changer_1 |
| mysql.infoschema |
| mysql.session |
| mysql.sys |
| reader_1 |
| reader_2 |
| reader_3 |
| reader_writer |
| root |
| |
+------------------+
26 rows in set (0.00 sec)
Na podstawie tego wyniku możemy przypuszczać; że w gruncie rzeczy role są w rzeczywistości samymi użytkownikami.
Następnie przypisanie uprawnień:
mysql> GRANT SELECT ON practice.name TO 'main_read_only';
Query OK, 0 rows affected (0.14 sec)
mysql> GRANT SELECT, INSERT ON practice.name TO 'main_read_write';
Query OK, 0 rows affected (0.07 sec)
mysql> GRANT UPDATE, DELETE ON practice.name TO 'main_changer';
Query OK, 0 rows affected (0.16 sec)
Krótkie przerywniki
Poczekaj minutę. Czy mogę się po prostu zalogować i samodzielnie wykonywać zadania za pomocą kont ról? W końcu są użytkownikami i mają wymagane uprawnienia.
Spróbujmy zalogować się do bazy danych praktyki z rolą main_changer:
:~$ mysql -u main_changer -p practice
Enter password:
ERROR 1045 (28000): Access denied for user 'main_changer'@'localhost' (using password: YES
Prosty fakt, że otrzymujemy monit o hasło, jest dobrą wskazówką, że nie możemy (przynajmniej w tej chwili). Jak pamiętasz, podczas ich tworzenia nie ustawiłem hasła do żadnej z ról.
Co ma do powiedzenia kolumna tabel systemowych mysql.user authentication_string?
mysql> SELECT User, authentication_string, password_expired
-> FROM mysql.user
-> WHERE User IN ('main_read_only', 'root', 'main_read_write', 'main_changer')\G
*************************** 1. row ***************************
User: main_changer
authentication_string:
password_expired: Y
*************************** 2. row ***************************
User: main_read_only
authentication_string:
password_expired: Y
*************************** 3. row ***************************
User: main_read_write
authentication_string:
password_expired: Y
*************************** 4. row ***************************
User: root
authentication_string: ***various_jumbled_mess_here*&&*&*&*##
password_expired: N
4 rows in set (0.00 sec)
Umieściłem użytkownika root wśród nazw ról dla sprawdzenia predykatu IN(), aby po prostu zademonstrować, że ma on ciąg_uwierzytelniania, w którym role nie mają.
Ten fragment dokumentacji CREATE ROLE dobrze to wyjaśnia:„Utworzona rola jest zablokowana, nie ma hasła i jest do niej przypisana domyślna wtyczka uwierzytelniająca. (Te atrybuty ról mogą być później zmienione za pomocą instrukcji ALTER USER przez użytkowników, którzy mają globalne uprawnienie CREATE USER)."
Wracając do aktualnego zadania, możemy teraz przypisywać role użytkownikom w oparciu o ich wymagany poziom uprawnień.
Zauważ, że w poleceniu nie ma klauzuli ON:
mysql> GRANT 'main_read_only' TO 'reader_1'@'localhost', 'reader_2'@'localhost', 'reader_3'@'localhost';
Query OK, 0 rows affected (0.13 sec)
mysql> GRANT 'main_read_write' TO 'reader_writer'@'localhost';
Query OK, 0 rows affected (0.16 sec)
mysql> GRANT 'main_changer', 'main_read_only' TO 'changer_1'@'localhost';
Query OK, 0 rows affected (0.13 sec)
Może to być mniej zagmatwane, jeśli użyjesz jakiejś „konwencji nazewnictwa ' podczas ustanawiania nazw ról (nie jestem świadomy, czy MySQL udostępnia je w tej chwili… Społeczność?), jeśli nie ma innego powodu niż wizualne odróżnienie ich od zwykłych użytkowników „nie pełniących roli”.
Wciąż pozostało trochę pracy do wykonania
To było bardzo proste, prawda?
Mniej nadmiarowy niż stary sposób przypisywania uprawnień.
Pozwólmy teraz tym użytkownikom pracować.
Widzimy przyznane uprawnienia dla użytkownika ze składnią SHOW GRANTS. Oto, co jest obecnie przypisane do konta użytkownika reader_1:
mysql> SHOW GRANTS FOR 'reader_1'@'localhost';
+------------------------------------------------------+
| Grants for [email protected] |
+------------------------------------------------------+
| GRANT USAGE ON *.* TO `reader_1`@`localhost` |
| GRANT `main_read_only`@`%` TO `reader_1`@`localhost` |
+------------------------------------------------------+
2 rows in set (0.02 sec)
Chociaż dostarcza to informacji wyjściowych, możesz „dostroić ' instrukcja zapewniająca jeszcze bardziej szczegółowe informacje na temat wszelkich dokładnych uprawnień, jakie zapewnia przypisana rola, poprzez umieszczenie klauzuli USING w instrukcji SHOW GRANTS i nazwanie przypisanych ról nazwą:
mysql> SHOW GRANTS FOR 'reader_1'@'localhost' USING 'main_read_only';
+-------------------------------------------------------------+
| Grants for [email protected] |
+-------------------------------------------------------------+
| GRANT USAGE ON *.* TO `reader_1`@`localhost` |
| GRANT SELECT ON `practice`.`name` TO `reader_1`@`localhost` |
| GRANT `main_read_only`@`%` TO `reader_1`@`localhost` |
+-------------------------------------------------------------+
3 rows in set (0.00 sec)
Po zalogowaniu się przez reader_1:
mysql> SELECT * FROM practice.name;
ERROR 1142 (42000): SELECT command denied to user 'reader_1'@'localhost' for table 'name'
Co u licha? Ten użytkownik otrzymał uprawnienia SELECT poprzez rolę main_read_only.
Aby to zbadać, odwiedź 2 nowe tabele w wersji 8, specjalnie dla ról.
Tabela mysql.role_edges pokazuje, jakie role zostały nadane dowolnym użytkownikom:
mysql> SELECT * FROM mysql.role_edges;
+-----------+-----------------+-----------+---------------+-------------------+
| FROM_HOST | FROM_USER | TO_HOST | TO_USER | WITH_ADMIN_OPTION |
+-----------+-----------------+-----------+---------------+-------------------+
| % | main_changer | localhost | changer_1 | N |
| % | main_read_only | localhost | changer_1 | N |
| % | main_read_only | localhost | reader_1 | N |
| % | main_read_only | localhost | reader_2 | N |
| % | main_read_only | localhost | reader_3 | N |
| % | main_read_write | localhost | reader_writer | N |
+-----------+-----------------+-----------+---------------+-------------------+
6 rows in set (0.00 sec)
Ale czuję, że inna dodatkowa tabela, mysql.default_roles, lepiej pomoże nam rozwiązać problemy SELECT dla użytkownika reader_1:
mysql> DESC mysql.default_roles;
+-------------------+----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+----------+------+-----+---------+-------+
| HOST | char(60) | NO | PRI | | |
| USER | char(32) | NO | PRI | | |
| DEFAULT_ROLE_HOST | char(60) | NO | PRI | % | |
| DEFAULT_ROLE_USER | char(32) | NO | PRI | | |
+-------------------+----------+------+-----+---------+-------+
4 rows in set (0.00 sec)
mysql> SELECT * FROM mysql.default_roles;
Empty set (0.00 sec)
Pusty zestaw wyników.
Okazuje się, że aby użytkownik mógł korzystać z roli - i ostatecznie z uprawnień - użytkownikowi musi zostać przypisana rola domyślna.
mysql> SET DEFAULT ROLE main_read_only TO 'reader_1'@'localhost', 'reader_2'@'localhost', 'reader_3'@'localhost';
Query OK, 0 rows affected (0.11 sec)
(Domyślną rolę można przypisać wielu użytkownikom w jednym poleceniu, jak powyżej…)
mysql> SET DEFAULT ROLE main_read_only, main_changer TO 'changer_1'@'localhost';
Query OK, 0 rows affected (0.10 sec)
(Użytkownik może mieć wiele domyślnych ról określonych jak w przypadku zmiany użytkownika_1…)
Użytkownik reader_1 jest teraz zalogowany...
mysql> SELECT CURRENT_USER();
+--------------------+
| CURRENT_USER() |
+--------------------+
| [email protected] |
+--------------------+
1 row in set (0.00 sec)
mysql> SELECT CURRENT_ROLE();
+----------------------+
| CURRENT_ROLE() |
+----------------------+
| `main_read_only`@`%` |
+----------------------+
1 row in set (0.03 sec)
Widzimy obecnie aktywną rolę, a także, że reader_1 może teraz wydawać polecenia SELECT:
mysql> SELECT * FROM practice.name;
+--------+------------+
| f_name | l_name |
+--------+------------+
| Jim | Dandy |
| Johhny | Applesauce |
| Ashley | Zerro |
| Ashton | Zerra |
| Ashmon | Zerro |
+--------+------------+
5 rows in set (0.00 sec)
Inne ukryte niuanse
Jest jeszcze jedna ważna część łamigłówki musimy zrozumieć.
Potencjalnie istnieją 3 różne „poziomy” lub „warianty” przypisania roli:
SET ROLE …;
SET DEFAULT ROLE …;
SET ROLE DEFAULT …;
Przyznam dodatkową rolę użytkownikowi reader_1, a następnie zaloguję się za pomocą tego użytkownika (nie pokazano):
mysql> GRANT 'main_read_write' TO 'reader_1'@'localhost';
Query OK, 0 rows affected (0.17 sec)
Ponieważ rola main_read_write ma uprawnienie INSERT, użytkownik reader_1 może teraz uruchomić to polecenie, prawda?
mysql> INSERT INTO name(f_name, l_name)
-> VALUES('Josh', 'Otwell');
ERROR 1142 (42000): INSERT command denied to user 'reader_1'@'localhost' for table 'name'
Co się tutaj dzieje?
To może pomóc...
mysql> SELECT CURRENT_ROLE();
+----------------------+
| CURRENT_ROLE() |
+----------------------+
| `main_read_only`@`%` |
+----------------------+
1 row in set (0.00 sec)
Przypomnijmy, początkowo ustawiliśmy użytkownikowi reader_1 jako domyślną rolę main_read_only. W tym miejscu musimy użyć jednego z tych odrębnych „poziomów” tego, co luźno nazywam 'ustawienie ról':
mysql> SET ROLE main_read_write;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT CURRENT_ROLE();
+-----------------------+
| CURRENT_ROLE() |
+-----------------------+
| `main_read_write`@`%` |
+-----------------------+
1 row in set (0.00 sec)
Teraz spróbuj ponownie INSERT:
mysql> INSERT INTO name(f_name, l_name)
-> VALUES('Josh', 'Otwell');
Query OK, 1 row affected (0.12 sec)
Jednak po wylogowaniu użytkownika reader_1, rola main_read_write nie będzie już aktywna, gdy użytkownik reader_1 zaloguje się ponownie. Chociaż użytkownik reader_1 ma przyznaną rolę main_read_write, nie jest ona domyślna.
Poznajmy teraz trzeci „poziom” „ustawiania ról”, USTAW DOMYŚLNĄ ROLĘ.
Załóżmy, że użytkownik reader_1 nie ma jeszcze przypisanych ról:
mysql> SHOW GRANTS FOR 'reader_1'@'localhost';
+----------------------------------------------+
| Grants for [email protected] |
+----------------------------------------------+
| GRANT USAGE ON *.* TO `reader_1`@`localhost` |
+----------------------------------------------+
1 row in set (0.00 sec)
Przyznajmy temu użytkownikowi 2 role:
mysql> GRANT 'main_changer', 'main_read_write' TO 'reader_1'@'localhost';
Query OK, 0 rows affected (0.07 sec)
Przypisz domyślną rolę:
mysql> SET DEFAULT ROLE ‘main_changer’ TO 'reader_1'@'localhost';
Query OK, 0 rows affected (0.17 sec)
Następnie po zalogowaniu użytkownika reader_1 ta domyślna rola jest aktywna:
mysql> SELECT CURRENT_ROLE();
+--------------------+
| CURRENT_ROLE() |
+--------------------+
| `main_changer`@`%` |
+--------------------+
1 row in set (0.00 sec)
Teraz przejdź do roli main_read_write:
mysql> SET ROLE 'main_read_write';
Query OK, 0 rows affected (0.01 sec)
mysql> SELECT CURRENT_ROLE();
+-----------------------+
| CURRENT_ROLE() |
+-----------------------+
| `main_read_write`@`%` |
+-----------------------+
1 row in set (0.00 sec)
Aby jednak powrócić do przypisanej domyślnej roli, użyj opcji SET ROLE DEFAULT, jak pokazano poniżej:
mysql> SET ROLE DEFAULT;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT CURRENT_ROLE();
+--------------------+
| CURRENT_ROLE() |
+--------------------+
| `main_changer`@`%` |
+--------------------+
1 row in set (0.00 sec)
Role nieprzyznane
Mimo że użytkownik changer_1 ma 2 role dostępne podczas sesji:
mysql> SELECT CURRENT_ROLE();
+-----------------------------------------+
| CURRENT_ROLE() |
+-----------------------------------------+
| `main_changer`@`%`,`main_read_only`@`%` |
+-----------------------------------------+
1 row in set (0.00 sec)
Co się stanie, jeśli spróbujesz ustawić użytkownikowi rolę, której nie przyznano?
mysql> SET ROLE main_read_write;
ERROR 3530 (HY000): `main_read_write`@`%` is not granted to `changer_1`@`localhost`
Odmowa.
Na wynos
Żaden system zarządzania użytkownikami nie byłby kompletny bez możliwości ograniczenia lub nawet usunięcia dostępu do niektórych operacji w razie potrzeby.
Mamy do dyspozycji polecenie SQL REVOKE, które usuwa uprawnienia użytkowników i ról.
Przypomnij sobie, że rola main_changer ma ten zestaw uprawnień, zasadniczo wszyscy użytkownicy, którym przyznano tę rolę, również:
mysql> SHOW GRANTS FOR main_changer;
+-----------------------------------------------------------------+
| Grants for [email protected]% |
+-----------------------------------------------------------------+
| GRANT USAGE ON *.* TO `main_changer`@`%` |
| GRANT UPDATE, DELETE ON `practice`.`name` TO `main_changer`@`%` |
+-----------------------------------------------------------------+
2 rows in set (0.00 sec)
mysql> REVOKE DELETE ON practice.name FROM 'main_changer';
Query OK, 0 rows affected (0.11 sec)
mysql> SHOW GRANTS FOR main_changer;
+---------------------------------------------------------+
| Grants for [email protected]% |
+---------------------------------------------------------+
| GRANT USAGE ON *.* TO `main_changer`@`%` |
| GRANT UPDATE ON `practice`.`name` TO `main_changer`@`%` |
+---------------------------------------------------------+
2 rows in set (0.00 sec)
Aby dowiedzieć się, jakich użytkowników wpłynęła ta zmiana, możemy ponownie odwiedzić tabelę mysql.role_edges:
mysql> SELECT * FROM mysql.role_edges WHERE FROM_USER = 'main_changer';
+-----------+--------------+-----------+-----------+-------------------+
| FROM_HOST | FROM_USER | TO_HOST | TO_USER | WITH_ADMIN_OPTION |
+-----------+--------------+-----------+-----------+-------------------+
| % | main_changer | localhost | changer_1 | N |
+-----------+--------------+-----------+-----------+-------------------+
1 row in set (0.00 sec)
Widzimy, że zmieniacz_użytkownika_1 nie ma już uprawnienia DELETE:
mysql> SHOW GRANTS FOR 'changer_1'@'localhost' USING 'main_changer';
+--------------------------------------------------------------------------+
| Grants for [email protected] |
+--------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO `changer_1`@`localhost` |
| GRANT UPDATE ON `practice`.`name` TO `changer_1`@`localhost` |
| GRANT `main_changer`@`%`,`main_read_only`@`%` TO `changer_1`@`localhost` |
+--------------------------------------------------------------------------+
3 rows in set (0.00 sec)
Wreszcie, jeśli musimy całkowicie pozbyć się roli, mamy do tego polecenie DROP ROLE:
mysql> DROP ROLE main_read_only;
Query OK, 0 rows affected (0.17 sec)
W przypadku zapytań do tabeli mysql.role_edges usunięto rolę main_read_only:
mysql> SELECT * FROM mysql.role_edges;
+-----------+-----------------+-----------+---------------+-------------------+
| FROM_HOST | FROM_USER | TO_HOST | TO_USER | WITH_ADMIN_OPTION |
+-----------+-----------------+-----------+---------------+-------------------+
| % | main_changer | localhost | changer_1 | N |
| % | main_read_write | localhost | reader_1 | N |
| % | main_read_write | localhost | reader_writer | N |
+-----------+-----------------+-----------+---------------+-------------------+
3 rows in set (0.00 sec)
(Bonus:ten fantastyczny film na YouTube był dla mnie świetnym źródłem wiedzy na temat ról).
Ten przykład tworzenia użytkownika, przypisywania ról i konfiguracji jest w najlepszym razie szczątkowy. Jednak role mają swój własny zestaw reguł, które sprawiają, że nie są trywialne. Mam nadzieję, że poprzez ten wpis na blogu rzuciłem światło na te obszary, które są mniej intuicyjne niż inne, umożliwiając czytelnikom lepsze zrozumienie potencjalnych zastosowań ról w ich systemach.
Dziękuję za przeczytanie.