Zarządzanie uprawnieniami w aplikacji webowej

0

Cześć,
w jaki sposób obsługujecie w aplikacjach webowych uprawnienia użytkowników? Przykładowo mogę nadać użytkownikowi dla każdego modułu uprawnienie do wyświetlania, dodawania, edycji, usuwania. Uprawnienia te musiałyby być sprawdzane zarówno po stronie front-endowej jak i back-endowej. Pobranie listy uprawnień przy logowaniu i cachowanie ma swoje minusy, po pierwsze po zmianie uprawnienia wymagany jest relogin, po drugie trzymanie takiego cache przy dużej ilości userow będzie powodowało zużycie RAM'u. Lepszym sposobem będzie napisanie metody która będzie przy każdym requescie sprawdzać czy user ma dane uprawnienie? to znowu wiąże się z każdorazowym strzałem do DB w każdym endpoincie.
Macie może jakieś pomysły? Jak obsługujecie tego typu sytuacje?

0

Prosta autoryzacja moze byc zrobiona w middleware per request, zeby ograniczyc ilosc strzalow do bazy mozna robic cache z inwalidacją przy zmianie uprawnień. Poza tym lepiej nie wymyślaj koła na nowo tylko skorzystaj z tego co jest dostarczone. Jeśli chces trzymać uwierzytelnianie i autoryzację u siebie to np. ASP .NET Core Identity. Alternatywnie użyj czegoś zewnętrznego, Keycloack, Azure AD, Auth0, Okta itp.

0

Nie mówię o uwierzytelnieniu, tylko o uprawnieniach zalogowanego użytkownika, wywołując jakąś metodę np. Save() chce sprawdzić czy użytkownik ma uprawnienie do Save danego obiektu, a kolejno uprawnienia do konkretnych obiektów, do jakichś części portalu itp. Dodam że działam na dość starej DB i muszę się do niej dostosować (pobieranie listy uprawnień).

0

Pobranie listy uprawnień przy logowaniu i cachowanie ma swoje minusy, po pierwsze po zmianie uprawnienia wymagany jest relogin, po drugie trzymanie takiego cache przy dużej ilości userow będzie powodowało zużycie RAM'u.

A ilu masz userów na raz lub dziennie w tej appce?

3

Pobranie listy uprawnień przy logowaniu i cachowanie ma swoje minusy, po pierwsze po zmianie uprawnienia wymagany jest relogin, po drugie trzymanie takiego cache przy dużej ilości userow będzie powodowało zużycie RAM'u. Lepszym sposobem będzie napisanie metody która będzie przy każdym requescie sprawdzać czy user ma dane uprawnienie? to znowu wiąże się z każdorazowym strzałem do DB w każdym endpoincie.

Ja wiem że C# to nie Java, ale odpaliłem sobie napisany w Javie kod który wygenerował HashMapę zawierają Stringa jako klucza i kolekcję z 3 Stringami jako wartość z milionem elementów, wyszłoże taka mapa waży 320 mb. A ile ramu mają serwery?

0

Już mniejsza o to ile mają RAMu i ile to będzie zajmować, liczyłem że może ktoś ma jakieś lepsze rozwiązanie, takie cachowanie ma też inny minus, jeśli user ma zapisane credentiale - (jest stale zalogowany) to zmiana uprawnień wymagałaby ponowne zainicjalizowanie takiej mapy uprawnień, cache. Więc musiała by być gdzieś dodatkowo zapisana informacje że trzeba zrobić ponową inicjalizację niestety tego już nie zapewnię w starych aplikacjach które są nadal w użytku.

0

@blane: nie jestem pewien czy dobrze rozumiem problem?

Na froncie nie musisz mieć cache praw, w każdym response możesz wysłać aktualną listę praw albo nawet zrobić na to osobny endpoint. Te dane mogą być brane z cache w backendzie, żadnego strzału do bazy nie potrzebujesz.

W backendzie również nie ma żadnego problemu aby w momencie zmiany uprawnień (dodanie nowej roli) poza strzałem do bazy od razu aktualizować cache. Nie rozumiem, w którym momencie jeżeli user jest zalogowany musiałbyś cokolwiek przebudować czy od nowa mapować.

Oczywiście wtedy porzucasz czytanie praw bezpośrednio z Claimów, ale zawsze możesz sobie to obsłużyć dodając nowe Policy z własnymi IAuthorizationRequirementem i AuthorizationHandlerem, gdzie sobie możesz odczytać prawo z dowolnego miejsca.

0

No ale muszę wiedzieć kiedy taki cache zaktualizować, jeśli uprawnienia zostaną zmienione w innej aplikacji to nie będę wiedział, że coś się zmieniło. O tym "IAuthorizationRequirementem i AuthorizationHandlerem" muszę poczytać bo nie wiem co to.

1

@blane: ok, czyli problemem tak naprawdę jest informacja, że nie wiesz kiedy uprawnienia się zmieniają. Masz gdzieś tam jakiegoś Redisa? Jak tak to zacznij pytać o prawa przez niego, wtedy Keyspace Notifications powie Ci kiedy aktualizować swój lokalny cache, albo w ogóle nie będziesz potrzebował lokalnego cache bo Redis da radę spokojnie to obsłużyć.

Jeżeli nie chcesz Redisa albo nie możesz to czy masz możliwość postawienia serwis pośredniczącego przed bazą danych? Taki serwis mógłby przechwycić każde zapytanie do bazy danych ze zmianą uprawnień, zrobić sobie z tego cache i wystawić notify/subscribe a zapytanie popchnąć dalej. Czyli coś co by po prostu zrobiło Redisową protezę :)

Wtedy taki serwis poinformuje każdy dowolny inny serwis, że musi sobie zaktualizować konkretny wpis w cache.

A jeżeli masz po prostu wolną bazkę/nie chce Ci się bawić z notify/subscribe to po prostu uderzaj do serwisu pośredniczącego za każdym razem. Powinno wyjść lepiej niż robić zapytanie bezpośrednio do bazy.

Zakładam, że masz tych zapytań sporo na sekundę skoro nie chcesz uderzać bezpośrednio do bazki. W jednym projekcie uderzanie o prawa szło do bazki kilka razy na request, wtedy wystarczyło zrobić sobie małą klasę scoped, która to cachowała na czas requestu.

Możesz też zrobić cache gdzie trzymasz informację przez 10-20 sekund czy ile tam biznes pozwoli. Jeżeli użytkownik w te 10-02 sekund robi 2-3 requesty to zrobisz jeden call do bazy zamiast trzech. W cache nie musisz mieć wszystkich danych wszystkich userów, możesz trzymać tylko te aktywne w ciągu ostatnich 10 sekund.

Jeżeli zapytanie na bazie muli to może jest problem z zapytaniem?

0

Przy starcie apki ładujesz listę uprawnień do cache. Po drugie to zapinasz uprawnienia na daną rolę użytkownika, żeby móc tym w jakikolwiek sposób zapanować, przy dużej ilości użytkowników nie możesz sobie pozwolić na customowe konfigurowanie uprawnien. Po zmianie uprawnień dla danej roli robisz inwalidacje. Przy dużej ilość użytkowników ram to będzie twoje najmniejsze zmartwienie.

0

A czemu nie chcesz tego zrobić w taki sposób np (uproszczony pseudokod):

public IActionResult Save()
{
  if(!loggedUser.HasRight(Rights.Save, "Documents"))
    return Unauthorized();
}

Gdzie HasRight to może być jakiś extension, który sprawdzi uprawnienia w bazie danych.

0

Nie powiedziałem, że nie chcę do tej pory tak robiłem, tylko uprawnienia przy logowaniu wpadały w cache. Strzały po uprawnienie do DB mnie przerażają trochę, bo user klika po aplikacji otwiera okno za oknem a tu za każdym razem leci strzał :P Ale teraz tak myślę czy właśnie nie wprowadzić jakiegoś reloadingu uprawnien w tle w końcu one aż tak często się nie będą zmieniały.

0

Ja nie widzę nic złego w strzałach do bazy po uprawnienia.

0

Wystąpił już problem z bazą danych? Jeśli nie to nie ma co za bardzo się przejmować dopóki nie masz kilkudziesięciu requestów na sekundę. Replika DB potem i też wystarczy. A cache to już w ogóle powinien do końca wystarczyć

0

Gdy wywołuję akcję kontrolera np. Save to wymagam odpowiedniej roli np. Admin i canUpdateUser. Inaczej dostaje 401. Role trzymam w bazie danych i tyle

1 użytkowników online, w tym zalogowanych: 0, gości: 1