Role oparte na kontroli dostępu (RBAC) są kluczowym elementem w tworzeniu aplikacji internetowych, w których różnym użytkownikom przypisuje się różne poziomy uprawnień. W aplikacjach opartych na JWT (JSON Web Tokens) zarządzanie tymi rolami jest szczególnie ważne, aby zapewnić odpowiedni dostęp do zasobów i ochronę przed nieautoryzowanym dostępem. W tej części omówimy sposób, w jaki można zaimplementować zabezpieczenia opartych na rolach w aplikacji ASP.NET Core Minimal API, wykorzystując mechanizmy autoryzacji JWT.

W pierwszym kroku do zabezpieczenia endpointów API wykorzystujemy atrybut [Authorize(Roles = "Manager")]. Ten atrybut zapewnia, że dostęp do danego zasobu mają tylko użytkownicy, którzy posiadają przypisaną rolę „Manager”. Przykładem jest poniższy fragment kodu:

csharp
app.MapGet("/manager", [Authorize(Roles = "Manager")] () => { return Results.Ok(new { Message = "This content is only for manager" }); });

W powyższym przypadku tylko użytkownicy z rolą „Manager” mają dostęp do tego endpointu. Tego rodzaju podejście jest stosowane w aplikacjach, które muszą ograniczyć dostęp do wrażliwych zasobów, takich jak dane zarządzania lub inne funkcje zarezerwowane dla określonej roli.

Kolejnym krokiem w implementacji RBAC w tej samej aplikacji jest stworzenie endpointu, który będzie dostępny zarówno dla użytkowników z rolą „Admin”, jak i „Manager”. Aby to osiągnąć, stosujemy następujący kod:

csharp
app.MapGet("/adminmanager", [Authorize(Roles = "Admin,Manager")] () => {
return Results.Ok(new { Message = "This content is only for admin and manager" }); });

W tym przypadku tylko użytkownicy posiadający jedną z dwóch ról — „Admin” lub „Manager” — będą mogli uzyskać dostęp do tego zasobu. Dzięki temu mechanizmowi aplikacja jest w stanie zapewnić odpowiednią separację dostępu do danych, w zależności od poziomu uprawnień użytkownika.

Po skonfigurowaniu endpointów, ważnym krokiem jest przygotowanie pliku .http, który będzie używany do testowania tych zabezpieczeń. Oto przykład pliku, który pozwala na testowanie dostępów do różnych endpointów API w oparciu o JWT tokeny:

http
@rbacapp_HostAddress = http://localhost:5289
@token = # Change the @rbacapp_HostAddress variable to match your application’s address

W tym pliku musimy zdefiniować adres aplikacji i miejsce na token JWT. Token ten, uzyskany po zalogowaniu użytkownika, jest następnie używany do autoryzacji dostępu do odpowiednich zasobów.

Testowanie autoryzacji opartych na rolach

Aby przetestować wprowadzone zmiany, należy przeprowadzić testy endpointów z wykorzystaniem klienta REST lub narzędzi takich jak Postman. Kluczowym krokiem w tym procesie jest użycie tokenu JWT, który zapewnia autoryzację. Aby to zrobić, użytkownik musi zalogować się przez endpoint /login, a następnie uzyskać token:

http
POST {{rbacapp_HostAddress}}/login
{ "Password": "pass123", "Username": "user3" }

Po uzyskaniu tokenu, możemy go wykorzystać w kolejnych żądaniach do zabezpieczonych zasobów. Przykład żądania do endpointu /manager z tokenem wygląda następująco:

http
GET {{rbacapp_HostAddress}}/manager Authorization: Bearer {{token}}

Jeśli użytkownik zaloguje się jako „Manager”, dostęp do tego zasobu będzie możliwy. W przeciwnym razie, użytkownicy, którzy nie posiadają roli „Manager”, otrzymają odpowiedź o błędzie z kodem HTTP 403, co oznacza, że nie mają odpowiednich uprawnień.

Warto również pamiętać o tym, że w przypadku użytkowników, którzy posiadają rolę „Admin”, będą mogli oni uzyskać dostęp do zasobów, które są dostępne zarówno dla administratorów, jak i menedżerów. Natomiast użytkownicy bez przypisanej roli nie będą mogli uzyskać dostępu do żadnego z tych zasobów.

Dodatkowe uwagi

W kontekście implementacji RBAC w aplikacji ASP.NET Core, istotne jest, aby pamiętać, że przyznawanie i modyfikowanie ról użytkowników powinno być ściśle kontrolowane i audytowane. Ponadto, podczas implementacji tego mechanizmu, warto zadbać o bezpieczeństwo tokenów JWT, które są kluczowym elementem autoryzacji. Należy również zapewnić odpowiednią ochronę tych tokenów w komunikacji oraz dbać o ich okresową wymianę.

Istnieje także konieczność stworzenia odpowiednich testów, które będą w stanie weryfikować poprawność działania systemu RBAC. Sprawdzenie, czy różni użytkownicy mogą uzyskać dostęp do zasobów tylko w ramach przypisanych ról, jest kluczowym elementem zapewnienia bezpieczeństwa aplikacji.

Przejście od Swagger do natywnej obsługi OpenAPI w ASP.NET Core 9.0

Wraz z wydaniem ASP.NET Core 9.0, proces integracji z OpenAPI stał się prostszy i bardziej zautomatyzowany, eliminując potrzebę korzystania z zewnętrznych narzędzi, takich jak Swagger. Natywna obsługa OpenAPI w tym frameworku zapewnia programistom nowoczesne, standaryzowane podejście do dokumentowania i testowania API. W tym rozdziale przyjrzymy się tej integracji, jak wygląda jej konfiguracja, oraz jakie korzyści niesie dla procesu tworzenia i testowania interfejsów API.

W wersji 9.0, korzystając z szablonu webapi, wsparcie dla OpenAPI jest domyślnie włączone. Oznacza to, że po utworzeniu nowego projektu API w ASP.NET Core 9.0, otrzymujemy od razu interaktywną dokumentację API bez potrzeby wykonywania dodatkowych kroków konfiguracyjnych. Jest to możliwe dzięki pakietowi Microsoft.AspNetCore.OpenApi, który generuje specyfikację OpenAPI na podstawie kodu API. Cały proces jest zintegrowany z mechanizmem wstrzykiwania zależności, co czyni go bardziej intuicyjnym i mniej zależnym od zewnętrznych narzędzi.

Konfiguracja OpenAPI w pliku Program.cs jest prosta. Kluczowym krokiem jest dodanie metody AddOpenApi() do kontenera serwisów, co umożliwia generowanie specyfikacji OpenAPI dla naszego API. Następnie, za pomocą warunku if (app.Environment.IsDevelopment()), dokumentacja API jest udostępniana jedynie w środowisku deweloperskim. To dobra praktyka, ponieważ ujawnienie szczegółów struktury API w produkcji może wiązać się z ryzykiem bezpieczeństwa. Specyfikacja OpenAPI jest dostępna pod ścieżką /openapi/v1.json, a jej zawartość jest zapisana w formacie JSON, co pozwala na dalsze przetwarzanie i generowanie dokumentacji za pomocą narzędzi zewnętrznych.

Przykład podstawowej konfiguracji w pliku Program.cs może wyglądać następująco:

csharp
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenApi(); var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.MapOpenApi(); }

Po uruchomieniu projektu możemy uzyskać dostęp do dokumentacji OpenAPI w przeglądarce, wchodząc na adres https://localhost:<port>/openapi/v1.json, gdzie <port> to port, na którym nasza aplikacja jest uruchomiona.

Warto dodać, że oprócz samej specyfikacji OpenAPI, można zainstalować dodatkowy pakiet, jak np. Scalar.AspNetCore, aby poprawić interaktywność dokumentacji. Dzięki temu możliwe jest wygodne testowanie API bezpośrednio z poziomu przeglądarki. Aby dodać Scalar UI do projektu, wystarczy zainstalować odpowiednią wersję pakietu:

lua
dotnet add package Scalar.AspNetCore --version 2.6.9

Następnie, aby zintegrować interfejs użytkownika Scalar, należy w pliku Program.cs dodać metodę MapScalarApiReference():

csharp
if (app.Environment.IsDevelopment()) { app.MapOpenApi(); app.MapScalarApiReference(); }

Po uruchomieniu aplikacji i przejściu pod adres https://localhost:<port>/scalar/, uzyskamy pełną, interaktywną dokumentację API, w której można łatwo testować poszczególne endpointy API.

Korzystanie z narzędzi takich jak Postman czy Insomnia w połączeniu z OpenAPI ułatwia proces testowania API. Wystarczy zaimportować specyfikację OpenAPI w formacie JSON do wybranego narzędzia, co pozwala na automatyczne generowanie zapytań do API na podstawie jego specyfikacji.

Jeśli chcemy jeszcze bardziej dostosować dokumentację API, możemy wprowadzić zmiany w pliku konfiguracyjnym OpenAPI. Na przykład, możemy dodać tytuł, wersję API, opis, a także niestandardowe metadane, które pozwolą lepiej opisać naszą aplikację i jej możliwości. Dodatkowo, warto pamiętać o dodaniu komentarzy XML w kodzie, które będą uwzględniane w generowanej specyfikacji OpenAPI, co umożliwia tworzenie bardziej szczegółowej dokumentacji.

W kontekście samego procesu programowania i utrzymania API warto zauważyć, że korzystanie z OpenAPI stanowi ważny element podejścia API-first. Dzięki tej metodzie, projektowanie API staje się bardziej systematyczne i ułatwia współpracę z innymi zespołami czy narzędziami. Standaryzacja opisana w specyfikacji OpenAPI sprzyja również lepszej interoperacyjności, co pozwala na łatwiejsze integrowanie API z różnymi systemami zewnętrznymi.

Warto również zauważyć, że wersja OpenAPI wbudowana w ASP.NET Core 9.0 jest lżejsza i bardziej wydajna niż wcześniejsze rozwiązania, takie jak Swagger. Dzięki temu proces dokumentowania API jest prostszy, szybszy i bardziej zintegrowany z frameworkiem, co znacznie redukuje zależności zewnętrzne i upraszcza konfigurację.