From 22cdef9b0f5d1bf8a8cfc86169be216fb0b552f8 Mon Sep 17 00:00:00 2001 From: Adrian Brenne Date: Wed, 4 Dec 2024 18:58:08 +0100 Subject: [PATCH 1/3] Det forventes tydeligvis rolle-claim i ID tokenet og ikke access tokenet. --- packages/adminpanel/Client/Pages/Index.razor | 25 ++++++++++++++++++- packages/adminpanel/Client/Program.cs | 2 ++ .../adminpanel/Client/Shared/NavMenu.razor | 2 +- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/packages/adminpanel/Client/Pages/Index.razor b/packages/adminpanel/Client/Pages/Index.razor index 65a63be3..a2c84370 100644 --- a/packages/adminpanel/Client/Pages/Index.razor +++ b/packages/adminpanel/Client/Pages/Index.razor @@ -1,13 +1,36 @@ @page "/" +@using System.Security.Claims +@inject AuthenticationStateProvider AuthenticationStateProvider Alvtime admin

Velkommen til Alvtime adminpanel!

- +@code{ + protected override async Task OnInitializedAsync() + { + var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); + var currentUser = authState.User; + + if (currentUser.Identity?.IsAuthenticated == true) + { + var roles = currentUser.Claims + .Where(c => c.Type == ClaimTypes.Role || c.Type == "roles") // Include custom role claim types if needed + .Select(c => c.Value) + .ToList(); + } + } } + + Velg hva du vil administrere i panelet til venstre. + + Du har ikke tilgang til å bruke dette panelet. + + + + Vennligst logg inn oppe til høyre. diff --git a/packages/adminpanel/Client/Program.cs b/packages/adminpanel/Client/Program.cs index 098e0973..bdbf398f 100644 --- a/packages/adminpanel/Client/Program.cs +++ b/packages/adminpanel/Client/Program.cs @@ -31,6 +31,8 @@ options.ProviderOptions.DefaultAccessTokenScopes.Add(builder.Configuration["ApiSettings:Scope"]!); options.ProviderOptions.LoginMode = "redirect"; options.ProviderOptions.Cache.CacheLocation = "localStorage"; + + options.UserOptions.RoleClaim = "roles"; }); builder.Services.AddHttpClientInterceptor(); diff --git a/packages/adminpanel/Client/Shared/NavMenu.razor b/packages/adminpanel/Client/Shared/NavMenu.razor index 2c8ffe2d..ce5d7897 100644 --- a/packages/adminpanel/Client/Shared/NavMenu.razor +++ b/packages/adminpanel/Client/Shared/NavMenu.razor @@ -1,6 +1,6 @@  Forside - + Ansatte Kunder From a23131957177f9787a96a89332762b7d0dee80fc Mon Sep 17 00:00:00 2001 From: Adrian Brenne Date: Wed, 11 Dec 2024 13:16:58 +0100 Subject: [PATCH 2/3] rbac access admin panel --- .../Authorization/CustomAccountFactory.cs | 35 ++ .../Client/Pages/Customers/Customer.razor | 469 +++++++++--------- .../Client/Pages/Customers/Customers.razor | 68 +-- .../Client/Pages/Employees/Employee.razor | 193 +++---- .../Client/Pages/Employees/Employees.razor | 74 +-- packages/adminpanel/Client/Pages/Index.razor | 25 +- packages/adminpanel/Client/Program.cs | 5 +- 7 files changed, 454 insertions(+), 415 deletions(-) create mode 100644 packages/adminpanel/Client/Authorization/CustomAccountFactory.cs diff --git a/packages/adminpanel/Client/Authorization/CustomAccountFactory.cs b/packages/adminpanel/Client/Authorization/CustomAccountFactory.cs new file mode 100644 index 00000000..6ebf4e8d --- /dev/null +++ b/packages/adminpanel/Client/Authorization/CustomAccountFactory.cs @@ -0,0 +1,35 @@ +namespace Alvtime.Adminpanel.Client.Authorization; + +using Microsoft.AspNetCore.Components.WebAssembly.Authentication; +using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal; +using System.Security.Claims; +using System.Text.Json; + +//https://auth0.com/blog/role-based-access-control-in-blazor-apps/ +public class CustomAccountFactory(IAccessTokenProviderAccessor accessor) : AccountClaimsPrincipalFactory(accessor) +{ + public override async ValueTask CreateUserAsync( + RemoteUserAccount account, + RemoteAuthenticationUserOptions options) + { + var userAccount = await base.CreateUserAsync(account, options); + var userIdentity = userAccount.Identity as ClaimsIdentity; + + if (userIdentity?.IsAuthenticated is true) + { + var roles = account.AdditionalProperties[userIdentity.RoleClaimType] as JsonElement?; + + if (roles?.ValueKind == JsonValueKind.Array) + { + userIdentity.TryRemoveClaim(userIdentity.Claims.FirstOrDefault(c => c.Type == userIdentity.RoleClaimType)); + + foreach (var element in roles.Value.EnumerateArray()) + { + userIdentity.AddClaim(new Claim(userIdentity.RoleClaimType, element.GetString())); + } + } + } + + return userAccount; + } +} \ No newline at end of file diff --git a/packages/adminpanel/Client/Pages/Customers/Customer.razor b/packages/adminpanel/Client/Pages/Customers/Customer.razor index e3cd09b3..c6546149 100644 --- a/packages/adminpanel/Client/Pages/Customers/Customer.razor +++ b/packages/adminpanel/Client/Pages/Customers/Customer.razor @@ -11,249 +11,254 @@ @if (_customer is not null) { - - - @Localizer["CustomersPage.Title"]/@_customer.Name + + + - - @_customer.Name - - - -
- - @Localizer["Common.CompanyInformation"] - @Localizer["Common.OrgNr"]: @_customer.OrgNr - @Localizer["Common.InvoiceAddress"]: @_customer.InvoiceAddress - - - @Localizer["Common.ContactInformation"] - @Localizer["Common.ContactPerson"]: @_customer.ContactPerson - @Localizer["Common.Email"]: @_customer.ContactEmail - @Localizer["Common.Telephone"]: @_customer.ContactPhone - -
- - - - - @Localizer["Common.Projects"] - - - - - + @Localizer["CustomersPage.Title"]/@_customer.Name + + + @_customer.Name + OnClick="@EditCustomer"> - - - +
+ + @Localizer["Common.CompanyInformation"] + @Localizer["Common.OrgNr"]: @_customer.OrgNr + @Localizer["Common.InvoiceAddress"]: @_customer.InvoiceAddress + + + @Localizer["Common.ContactInformation"] + @Localizer["Common.ContactPerson"]: @_customer.ContactPerson + @Localizer["Common.Email"]: @_customer.ContactEmail + @Localizer["Common.Telephone"]: @_customer.ContactPhone + +
+ + + - - - -
- - - -
-
- - - - - @Localizer["Common.Tasks"] - - - - - - - - - - + + @Localizer["Common.Projects"] + + + + + + + + + + + + + + + + + + + + + + - - - - - - @(context.Item.Imposed ? @Localizer["Common.Yes"] : @Localizer["Common.No"]) - - - - - @(context.Item.Locked ? @Localizer["Common.Yes"] : @Localizer["Common.No"]) - - - - - - - - - - - - - - - @Localizer["Common.HourRates"] - - - - - - - - - - + + @Localizer["Common.Tasks"] + + + + + + + + + + + + + + + + @(context.Item.Imposed ? @Localizer["Common.Yes"] : @Localizer["Common.No"]) + + + + + @(context.Item.Locked ? @Localizer["Common.Yes"] : @Localizer["Common.No"]) + + + + + + + + + + + + + - - + + + + + + + + + + + + +
+ Du har ikke tilgang til å se dette innholdet. +
} else { diff --git a/packages/adminpanel/Client/Pages/Customers/Customers.razor b/packages/adminpanel/Client/Pages/Customers/Customers.razor index 19108d9c..065fa91c 100644 --- a/packages/adminpanel/Client/Pages/Customers/Customers.razor +++ b/packages/adminpanel/Client/Pages/Customers/Customers.razor @@ -12,33 +12,35 @@ @Localizer["CustomersPage.Title"] -@Localizer["CustomersPage.Title"] - - - @Localizer["CustomersPage.AddCustomer"] - - - - - - @if (FilteredCustomers is not null) - { - @foreach (var customer in FilteredCustomers) - { + + + @Localizer["CustomersPage.Title"] + + + @Localizer["CustomersPage.AddCustomer"] + + + + + + @if (FilteredCustomers is not null) + { + @foreach (var customer in FilteredCustomers) + { - } - } - + } + } + +
+ + + Du har ikke tilgang til å se dette innholdet. + +
@code { [Inject] private HttpClient HttpClient { get; set; } diff --git a/packages/adminpanel/Client/Pages/Employees/Employee.razor b/packages/adminpanel/Client/Pages/Employees/Employee.razor index 3b58e452..d3972190 100644 --- a/packages/adminpanel/Client/Pages/Employees/Employee.razor +++ b/packages/adminpanel/Client/Pages/Employees/Employee.razor @@ -9,105 +9,110 @@ @if (_employee is not null) { - - @_employee.Name ?? @Localizer["EmployeePage.Details"] - - - @_employee.Name - - - - -
- - @if (_employee is null) - { - // insert avatar image - - - - } - else - { - // insert placeholder avatar - - @TextUtilities.GetInitialsFromName(_employee.Name, null) - - } - -
- - - @Localizer["Common.StartDate"]: @(_employee.StartDate.HasValue ? _employee.StartDate.Value.ToString("dd.MM.yyyy") : "") - - - @Localizer["Common.EndDate"]: @(_employee.EndDate.HasValue ? _employee.EndDate.Value.ToString("dd.MM.yyyy") : "") - - - @Localizer["Common.Email"]: @_employee.Email - - - @Localizer["Common.EmployeeId"]: @_employee.EmployeeId - - - - - - - @Localizer["Common.EmploymentRate"] - - - + + + + @_employee.Name ?? @Localizer["EmployeePage.Details"] + + + @_employee.Name + OnClick="@EditEmployee"> - - - + +
+ + @if (_employee is null) + { + // insert avatar image + + + + } + else + { + // insert placeholder avatar + + @TextUtilities.GetInitialsFromName(_employee.Name, null) + + } + +
+ + + @Localizer["Common.StartDate"]: @(_employee.StartDate.HasValue ? _employee.StartDate.Value.ToString("dd.MM.yyyy") : "") + + + @Localizer["Common.EndDate"]: @(_employee.EndDate.HasValue ? _employee.EndDate.Value.ToString("dd.MM.yyyy") : "") + + + @Localizer["Common.Email"]: @_employee.Email + + + @Localizer["Common.EmployeeId"]: @_employee.EmployeeId + + + + + - - - -
-
-
+ Items="_employee.EmploymentRates" + MultiSelection="@_multiSelection" + @bind-selectedItem="_selectedEmploymentRate" + @bind-SelectedItems="_employmentRateSelectionList" + Filterable="false" + FooterClass="d-none"> + + @Localizer["Common.EmploymentRate"] + + + + + + + + + + + + + + + + Du har ikke tilgang til å se dette innholdet. +
} else { diff --git a/packages/adminpanel/Client/Pages/Employees/Employees.razor b/packages/adminpanel/Client/Pages/Employees/Employees.razor index 85f604fe..4ebb0c92 100644 --- a/packages/adminpanel/Client/Pages/Employees/Employees.razor +++ b/packages/adminpanel/Client/Pages/Employees/Employees.razor @@ -11,42 +11,48 @@ @Localizer["EmployeesPage.Title"] -@Localizer["EmployeesPage.Employees"] - - @Localizer["EmployeesPage.AddEmployee"] - - + + + @Localizer["EmployeesPage.Employees"] + + @Localizer["EmployeesPage.AddEmployee"] + + + + + + + @if (FilteredEmployees is not null) + { + @foreach (var employee in FilteredEmployees) + { + + } + } + + + Du har ikke tilgang til å se dette innholdet. - - - @if (FilteredEmployees is not null) - { - @foreach (var employee in FilteredEmployees) - { - - } - } - + @code { [Inject] private HttpClient HttpClient { get; set; } diff --git a/packages/adminpanel/Client/Pages/Index.razor b/packages/adminpanel/Client/Pages/Index.razor index a2c84370..f40058d2 100644 --- a/packages/adminpanel/Client/Pages/Index.razor +++ b/packages/adminpanel/Client/Pages/Index.razor @@ -1,37 +1,14 @@ @page "/" -@using System.Security.Claims -@inject AuthenticationStateProvider AuthenticationStateProvider Alvtime admin

Velkommen til Alvtime adminpanel!

-@code{ - protected override async Task OnInitializedAsync() - { - var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); - var currentUser = authState.User; - - if (currentUser.Identity?.IsAuthenticated == true) - { - var roles = currentUser.Claims - .Where(c => c.Type == ClaimTypes.Role || c.Type == "roles") // Include custom role claim types if needed - .Select(c => c.Value) - .ToList(); - } - } } - Velg hva du vil administrere i panelet til venstre. - Du har ikke tilgang til å bruke dette panelet. - - - - - - Vennligst logg inn oppe til høyre. + Enten så er du ikke logget inn, eller så har du ikke tilgang. \ No newline at end of file diff --git a/packages/adminpanel/Client/Program.cs b/packages/adminpanel/Client/Program.cs index bdbf398f..c15b827d 100644 --- a/packages/adminpanel/Client/Program.cs +++ b/packages/adminpanel/Client/Program.cs @@ -3,6 +3,7 @@ using Alvtime.Adminpanel.Client; using Alvtime.Adminpanel.Client.Authorization; using Alvtime.Adminpanel.Client.ErrorHandling; +using Microsoft.AspNetCore.Components.WebAssembly.Authentication; using MudBlazor.Services; using Toolbelt.Blazor.Extensions.DependencyInjection; @@ -31,11 +32,13 @@ options.ProviderOptions.DefaultAccessTokenScopes.Add(builder.Configuration["ApiSettings:Scope"]!); options.ProviderOptions.LoginMode = "redirect"; options.ProviderOptions.Cache.CacheLocation = "localStorage"; - options.UserOptions.RoleClaim = "roles"; }); builder.Services.AddHttpClientInterceptor(); builder.Services.AddMudServices(); +builder.Services.AddScoped(typeof(AccountClaimsPrincipalFactory), + typeof(CustomAccountFactory)); + await builder.Build().RunAsync(); From c4bcc0d4960a81b5965a1303b29c77789ec07019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A5l=20B=C3=B8ckmann?= Date: Thu, 12 Dec 2024 14:08:24 +0100 Subject: [PATCH 3/3] Check if roles claim exist --- .../Authorization/CustomAccountFactory.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/adminpanel/Client/Authorization/CustomAccountFactory.cs b/packages/adminpanel/Client/Authorization/CustomAccountFactory.cs index 6ebf4e8d..5ea255fc 100644 --- a/packages/adminpanel/Client/Authorization/CustomAccountFactory.cs +++ b/packages/adminpanel/Client/Authorization/CustomAccountFactory.cs @@ -15,17 +15,22 @@ public override async ValueTask CreateUserAsync( var userAccount = await base.CreateUserAsync(account, options); var userIdentity = userAccount.Identity as ClaimsIdentity; - if (userIdentity?.IsAuthenticated is true) + if (userIdentity?.IsAuthenticated is not true) return userAccount; + + if (account.AdditionalProperties.TryGetValue(userIdentity.RoleClaimType, out var rolesObj) && rolesObj is JsonElement roles && roles.ValueKind == JsonValueKind.Array) { - var roles = account.AdditionalProperties[userIdentity.RoleClaimType] as JsonElement?; - - if (roles?.ValueKind == JsonValueKind.Array) + var roleClaim = userIdentity.Claims.FirstOrDefault(c => c.Type == userIdentity.RoleClaimType); + if (roleClaim != null) { - userIdentity.TryRemoveClaim(userIdentity.Claims.FirstOrDefault(c => c.Type == userIdentity.RoleClaimType)); + userIdentity.TryRemoveClaim(roleClaim); + } - foreach (var element in roles.Value.EnumerateArray()) + foreach (var element in roles.EnumerateArray()) + { + var role = element.GetString(); + if (!string.IsNullOrEmpty(role)) { - userIdentity.AddClaim(new Claim(userIdentity.RoleClaimType, element.GetString())); + userIdentity.AddClaim(new Claim(userIdentity.RoleClaimType, role)); } } }