Opgave: Authorization - using Cookies, Claims, Roles and Encryptions


I denne opgave tager vi udgangspunkt i eksemplet ItemsRazor ItemRazorV7.zip





I en tidligere opgave blev der implementeret Cookie-baseret Autentifikation, dvs en login-funktion. I denne opgave skal der implementeres rollebaseret Autorisation. Helt konkret skal der implementeres en løsning, hvor der kan oprettes nye brugere (User), men denne funktion må kun være tilgængelig hvis man er logget på som administrator. Selve linket til CreateUser siden skal kun være synlig for administrator og prøver man at gå direkte til siden, skal man omdirigeres til en AccesDenied side.

 

Step 1 (CreateUserPage - Razor Page)
Opret en ny Razor Page CreateUser i mappen Admin.

 

Step 2 (CreateUser.cshtml.cs)
Først skal vi sikre siden mod ikke-autoriseret adgang. Det gøres ved kun at tillade brugere med rollen "admin" adgang.

  1. Tilføj følgende annotations til klassen: [Authorize(Roles = "admin")] (skal stå lige over public class ...)

  2. Tilføj en reference til UserService:

    private UserService _userService;


    Husk, at initialisere servicen i konstruktøren!

  3. Tilføj 2 properties: UserName og Password af typen string. UserName skal annoteres med [BindProperty] og Password med [BindProperty, DataType(DataType.Password)]

  4. Til sidst skal OnPost( ) implementeres - der skal tilføjes en ny User ved at kalde AddUser(new User(UserName, Password)) _userService:

    public IActionResult OnPost()
    {
    if (!ModelState.IsValid)
    {
    return Page();
    }
    _userService.AddUser(new User(UserName, Password));
    return RedirectToPage("/Item/GetAllItems");
    }

 

Step 3 (CreateUser.cshtml)
Her tilføjes html-elementer så administrator kan indtaste UserName og Password mm ala:

<h1>Create a new User</h1>

<div class="row">
<div class="col-md-4">
<form method="post">
<div class="form-group">
<label asp-for="@Model.UserName" class="control-lable"></label>
<input asp-for="@Model.UserName" class="form-control" />
</div>
<div class="form-group">
<label asp-for="@Model.Password" class="control-lable"></label>
<input asp-for="@Model.Password" class="form-control" />

</div>

<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-page="/Item/GetAllItems">Back to List</a>
</div>

 

Step 4 (UserService)
Tilføj en ny metode AddUser( ) til klassen UserService så der kan tilføjes (og gemmes) en ny User:

public void AddUser(User user)
{
Users.Add(user);
JsonFileService.SaveJsonObjects(Users);
}

 


Step 5 (LogInPage.cshtml.cs)
I step 2 blev siden CreateUsers sikret mod ikke-autoriseret adgang for alle brugere der ikke har rollen: "admin". Nu skal vi sikre at administrator for tildelt denne rolle når han logger på.

I OnPost-metoden findes et if-statement der tester om UserName og Password matcher en user i listen users. Hvis der er et match oprettes en liste af Claim-objekter og den initialiseres med et Claim-objekt hvor ClaimTypes.Name sættes til UserName.

Tilføj et if-statement der tester om UserName er "admin" - hvis det er sandt tilføjes et nyt Claim-objekt, hvor ClaimTypes.Role sættes til "admin":

if (UserName == "admin") claims.Add(new Claim(ClaimTypes.Role, "admin"));

Nu er administrator tildelt rollen "admin" og kan tilgå siden CreateUser.


Step 6 (MockUsers, User.json)
Tilføj en administrator (admin) user til MockUsers og User.json:

fx:

new User("admin", "secret"), og

{
"UserName": "admin",
"Password": "secret"
},

.

Step 7 (_Layout.cshtml)
Der skal nu tilføjes et link til "Create User". Linket skal kun være synligt for administrator.

Linie 44: Tilføj følgende kode:

@{
if (LogInPageModel.LoggedInUser != null && LogInPageModel.LoggedInUser.UserName == "admin")
{
<li class="nav-item">
<a class="nav-link" asp-area="" asp-page="/Admin/CreateUser">Create User</a>
</li>
}



Step 8 (Afprøv)
Afprøv, og test at det ikke er muligt at navigere til CreateUser ved at taste adressen ind direkte: "https://localhost:44374/Admin/CreateUser" og at linket kun er synligt for administrator.
Bemærk: Hvis der tastes "https://localhost:44374/Admin/CreateUser" og bruger ikke er administrator returneres en "grim" fejlside.

 


Step 9 (Account, AccessDenied pages)
Den grimme fejlside fra step 8 skal fjernes.

Opret en ny mappe: Account med en ny page: AccessDenied.
Siden skal vise en passende fejl meddelelse ala:

<br/>
<br/>
<h1>Access Denied</h1>
<h3>You have to be logged in as admin</h3>

 

Step 10 (Afprøv)
Afprøv at implementeringen virker.



Kryptering af password

Nu da der både er implementeret Autentifikation og Autorisation i vores lille App, er det tid til at password bliver krypteret. Det gøres ved brug af PasswordHasher fra Microsoft.AspNetCore.Identity

 

Step 11 (MockUsers)
Først skal vi have krypteret vores password i MockUser det gøres ved kald af metoden: HashPassword fra klassen PaswordHasher, ala:


private static List<User> users = new List<User>() {
new User("admin", passwordHasher.HashPassword(null, "secret")),
new User("heho", passwordHasher.HashPassword(null, "123")),
new User("jaef", passwordHasher.HashPassword(null, "123"))
};



Step 12 (Users.json)
Overskriv den gamle ukrypterede json fil med data fra MockUser (via UserService klassen).
Hint:

Users = MockUsers.GetMockUsers();
//Users = JsonFileService.GetJsonObjects().ToList();
JsonFileService.SaveJsonObjects(Users);




Step 13 (LogInPage.cshtml.cs)
I stedet for at teste om Password == userPassword skal der nu benyttes PasswordHasher metoden VerifyHashedPassword - tilret koden (linie 41->) med:

 

if (UserName == user.UserName)
{
var passwordHasher = new PasswordHasher<string>();
if (passwordHasher.VerifyHashedPassword(null, user.Password, Password) == PasswordVerificationResult.Success)
{
.....}

 

Step 14 (CreateUser.cshtml.cs)
CreateUser skal opdateres (refaktoreres) til at benytte PasswordHasher.

private PasswordHasher<string> passwordHasher;

Og initialiser referencen med et nyt objekt i konstruktøren.


_userService.AddUser(new User(UserName, passwordHasher.HashPassword(null, Password)));

 

Step 15 (Afprøv)
Afprøv at implementeringen med kryptering virker - og tjek at password i User.json er krypteret!.



God fornøjelse!
Henrik Høltzer