Compare commits

...

1 Commits

@ -1,12 +1,12 @@
<Router AppAssembly="@typeof(App).Assembly">
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<CascadingAuthenticationState>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</CascadingAuthenticationState>
</NotFound>
</Router>

@ -7,6 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="Blazored.LocalStorage" Version="4.4.0" />
<PackageReference Include="Blazorise" Version="1.4.1" />
<PackageReference Include="Blazorise.Bootstrap" Version="1.4.0" />

@ -1,4 +1,3 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.8.34330.188

@ -0,0 +1,9 @@
namespace HeartTrack.Models.Authentification
{
public class AppUser
{
public string Password { get; set; }
public List<string> Roles { get; set; }
public string UserName { get; set; }
}
}

@ -0,0 +1,9 @@
namespace HeartTrack.Models.Authentification
{
public class CurrentUser
{
public Dictionary<string, string> Claims { get; set; }
public bool IsAuthenticated { get; set; }
public string UserName { get; set; }
}
}

@ -0,0 +1,13 @@
using System.ComponentModel.DataAnnotations;
namespace HeartTrack.Models.Authentification
{
public class LoginRequest
{
[Required]
public string Password { get; set; }
[Required]
public string UserName { get; set; }
}
}

@ -1,4 +1,6 @@
@page "/tickets/add"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Roles = "admin")]
@* <AuthorizeView Context="authContext" Roles="admin">
<Authorized > *@

@ -0,0 +1,28 @@
@page "/login"
@layout AuthLayout
<h1 class="h2 font-weight-normal login-title">
Login
</h1>
<EditForm class="form-signin" OnValidSubmit="OnSubmit" Model="loginRequest">
<DataAnnotationsValidator />
<div class="form-group mb-4 text-center">
<label for="inputUsername">Username</label>
<InputText id="inputUsername" class="form-control max-width-input mx-auto" @bind-Value="loginRequest.UserName" autofocus />
<ValidationMessage For="@(() => loginRequest.UserName)" />
</div>
<div class="form-group mb-4 text-center">
<label for="inputPassword">Password</label>
<InputText type="password" id="inputPassword" class="form-control max-width-input mx-auto" @bind-Value="loginRequest.Password" />
<ValidationMessage For="@(() => loginRequest.Password)" />
</div>
<div class="text-center">
<button class="btn btn-lg btn-success max-width-input" type="submit">SignIn</button>
</div>
<p class="mt-3 text-danger">@errorMessage</p>
</EditForm>

@ -0,0 +1,43 @@
using HeartTrack.Models.Authentification;
using HeartTrack.Services.AuthentificationService;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Localization;
namespace HeartTrack.Pages.Authentification
{
public partial class Login
{
[Inject]
public CustomStateProvider AuthStateProvider { get; set; }
[Inject]
public NavigationManager NavigationManager { get; set; }
[Inject]
public IStringLocalizer<Login> Localizer { get; set; }
private string errorMessage { get; set; }
[Inject]
public ILogger<Login> Logger { get; set; }
private LoginRequest loginRequest { get; set; } = new LoginRequest();
private async Task OnSubmit()
{
errorMessage = null;
try
{
Logger.LogInformation($"Tentative de connexion pour l'utilisateur : {loginRequest.UserName}");
await Task.Run(() => AuthStateProvider.Login(loginRequest));
Logger.LogInformation($"L'utilisateur {loginRequest.UserName} s'est connecté avec succès");
NavigationManager.NavigateTo("");
}
catch (Exception ex)
{
errorMessage = ex.Message;
Logger.LogError(ex, $"Échec de connexion pour l'utilisateur : {loginRequest.UserName}");
}
}
}
}

@ -1,6 +1,19 @@
@page "/"
@using System.Globalization
<AuthorizeView>
<Authorized>
<h1>Hello @context.User.Identity.Name !!</h1>
<p>Welcome to Blazor Learner.</p>
<ul>
@foreach (var claim in context.User.Claims)
{
<li>@claim.Type: @claim.Value</li>
}
</ul>
<PageTitle>Global View</PageTitle>
<h1>Global View</h1>
@ -14,3 +27,12 @@ This is the global statistics of our website.
</div>
</Authorized>
<Authorizing>
<h1>Loading ...</h1>
</Authorizing>
<NotAuthorized>
<h1>Authentication Failure!</h1>
<p>You're not signed in.</p>
</NotAuthorized>
</AuthorizeView>

@ -1,5 +1,7 @@
@page "/tickets"
@using HeartTrack.Models
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Roles = "admin")]
<PageTitle>Tickets</PageTitle>

@ -5,6 +5,7 @@ using HeartTrack.Models;
using HeartTrack.Services;
using HeartTrack.Services.TicketDataService;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Authorization;
using MudBlazor;
using System;
using static MudBlazor.CategoryTypes;
@ -31,6 +32,7 @@ namespace HeartTrack.Pages
[Inject]
public NavigationManager NavigationManager { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
// Do not treat this action if is not the first render
@ -80,12 +82,12 @@ namespace HeartTrack.Pages
private void OnView(int id)
{
NavigationManager.NavigateTo("tickets/view/"+id);
NavigationManager.NavigateTo("tickets/view/"+id, true);
}
private void OnNavigateOnAddClicked()
{
NavigationManager.NavigateTo("tickets/add");
NavigationManager.NavigateTo("tickets/add", true);
}
private async void OnDelete(Ticket t)

@ -1,6 +1,6 @@
@page "/tickets/view/{Id:int}"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Roles = "admin")]
<h3>Ticket number @Id</h3>

@ -10,17 +10,10 @@ using HeartTrack.Services.ActivityDataService;
using HeartTrack.Services.ReportDataService;
using HeartTrack.Services.UserDataService;
using HeartTrack.Services.TicketDataService;
using Blazorise;
using Blazorise.Bootstrap;
using Blazorise.Icons.FontAwesome;
using Microsoft.AspNetCore.Localization;
using System.Globalization;
using Microsoft.Extensions.Options;
using Blazored.LocalStorage;
using HeartTrack.Services;
using Microsoft.AspNetCore.Components.Authorization;
using MudBlazor.Services;
using HeartTrack.Services.TicketDataServiceFactice;
using HeartTrack.Services.AuthentificationService;
var builder = WebApplication.CreateBuilder(args);
@ -30,6 +23,13 @@ builder.Services.AddMudServices();
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddOptions();
builder.Services.AddAuthorizationCore();
builder.Services.AddScoped<CustomStateProvider>();
builder.Services.AddScoped<AuthenticationStateProvider>(s => s.GetRequiredService<CustomStateProvider>());
builder.Services.AddScoped<IAuthService, AuthService>();
builder.Services.AddHttpClient();
// Add Services
@ -115,6 +115,7 @@ if (options?.Value != null)
app.UseRequestLocalization(options.Value);
}
// Add the controller to the endpoint
app.UseEndpoints(endpoints =>
{

@ -0,0 +1,61 @@
using HeartTrack.Models;
using HeartTrack.Models.Authentification;
using Microsoft.AspNetCore.Components;
using System.Security.Claims;
namespace HeartTrack.Services.AuthentificationService
{
public class AuthService : IAuthService
{
private readonly List<AppUser> users;
[Inject]
private HttpClient Http { get; set; }
[Inject]
private NavigationManager NavigationManager { get; set; }
private readonly ILogger<AuthService> _logger;
public AuthService(IDataService dataService, ILogger<AuthService> logger)
{
users = new List<AppUser>
{
new AppUser { UserName = "Admin", Password = "123456", Roles = new List<string> { "admin" } }
};
_logger = logger;
}
public async Task<CurrentUser> GetUser(string username, string password)
{
var user = users.FirstOrDefault(w => w.UserName == username && BCrypt.Net.BCrypt.Verify(password, w.Password));
if (user == null)
{
throw new Exception("User name or password invalid !");
}
var claims = new List<Claim>();
claims.AddRange(user.Roles.Select(s => new Claim(ClaimTypes.Role, s)));
return await Task.FromResult(new CurrentUser
{
IsAuthenticated = true,
UserName = user.UserName,
Claims = claims.ToDictionary(c => c.Type, c => c.Value)
});
}
public async Task Login(LoginRequest loginRequest)
{
var user = users.FirstOrDefault(w => w.UserName == loginRequest.UserName && BCrypt.Net.BCrypt.Verify(loginRequest.Password, w.Password));
if (user == null)
{
_logger.LogError($"�chec de connexion pour l'utilisateur : {loginRequest.UserName}");
throw new Exception("User name or password invalid !");
}
}
}
}

@ -0,0 +1,65 @@
using Blazored.LocalStorage;
using HeartTrack.Models.Authentification;
using Microsoft.AspNetCore.Components.Authorization;
using System.Security.Claims;
namespace HeartTrack.Services.AuthentificationService
{
public class CustomStateProvider : AuthenticationStateProvider
{
private readonly IAuthService _authService;
private readonly ILocalStorageService _localStorage;
public CurrentUser? currentUser { get; set; }
public CustomStateProvider(IAuthService authService, ILocalStorageService localStorage)
{
_authService = authService;
_localStorage = localStorage;
}
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
await RehydrateUserStateAsync();
var identity = new ClaimsIdentity();
if (currentUser?.IsAuthenticated == true)
{
var claims = new[] { new Claim(ClaimTypes.Name, currentUser.UserName) }
.Concat(currentUser.Claims.Select(c => new Claim(c.Key, c.Value)));
identity = new ClaimsIdentity(claims, "Server authentication");
}
return new AuthenticationState(new ClaimsPrincipal(identity));
}
public async Task Login(LoginRequest loginParameters)
{
_authService.Login(loginParameters);
var user = await _authService.GetUser(loginParameters.UserName, loginParameters.Password);
currentUser = user;
await _localStorage.SetItemAsync("currentUser", currentUser);
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
}
public async Task Logout()
{
await _localStorage.RemoveItemAsync("currentUser");
currentUser = null;
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
}
private async Task RehydrateUserStateAsync()
{
currentUser = await _localStorage.GetItemAsync<CurrentUser>("currentUser");
if (currentUser != null && currentUser.IsAuthenticated)
{
// Utilisateur authentifié
}
else
{
currentUser = new CurrentUser();
}
}
}
}

@ -0,0 +1,11 @@
using HeartTrack.Models.Authentification;
namespace HeartTrack.Services.AuthentificationService
{
public interface IAuthService
{
Task<CurrentUser> GetUser(string username, string password);
Task Login(LoginRequest loginRequest);
}
}

@ -1,5 +1,6 @@
using Blazored.LocalStorage;
using HeartTrack.Models;
using HeartTrack.Services.AuthentificationService;
using Microsoft.AspNetCore.Components;
using static MudBlazor.CategoryTypes;
@ -11,17 +12,20 @@ namespace HeartTrack.Services
private readonly ILocalStorageService _localStorage;
private readonly NavigationManager _navigationManager;
private readonly IWebHostEnvironment _webHostEnvironment;
private readonly CustomStateProvider _customStateProvider;
public DataLocalService(
ILocalStorageService localStorage,
HttpClient http,
IWebHostEnvironment webHostEnvironment,
NavigationManager navigationManager)
NavigationManager navigationManager,
CustomStateProvider customStateProvider)
{
_localStorage = localStorage;
_http = http;
_webHostEnvironment = webHostEnvironment;
_navigationManager = navigationManager;
_customStateProvider = customStateProvider;
}
public async Task Add(UserModel model)

@ -1,13 +1,17 @@
using System;
using Blazored.LocalStorage;
using HeartTrack.Models;
using HeartTrack.Services.AuthentificationService;
using HeartTrack.Services.TicketDataService;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Authorization;
namespace HeartTrack.Services.TicketDataServiceFactice
{
public class TicketDataServiceFactice : ITicketDataService
{
[CascadingParameter]
private Task<AuthenticationState> AuthenticationState { get; set; }
[Inject]
private HttpClient _clientHttp { get; set; }
@ -21,13 +25,14 @@ namespace HeartTrack.Services.TicketDataServiceFactice
private String EmplacementJson { get; set; }
public TicketDataServiceFactice(HttpClient clientHttp, ILocalStorageService localStorage, NavigationManager navigationManager)
public TicketDataServiceFactice(HttpClient clientHttp, ILocalStorageService localStorage, NavigationManager navigationManager,CustomStateProvider AuthStateProvider)
{
this._clientHttp = clientHttp;
this._localStorage = localStorage;
this._navigationManager = navigationManager;
this.EmplacementLocalStorage = "ticketsData";
this.EmplacementJson = $"{_navigationManager.BaseUri}data/fake-tickets.json";
this.AuthenticationState = AuthStateProvider.GetAuthenticationStateAsync();
}
public async Task AddTicket(Ticket t)

@ -0,0 +1,6 @@
@inherits LayoutComponentBase
<div class="main">
<div class="content px-4">
@Body
</div>
</div>

@ -17,38 +17,15 @@
<span class="navbat-toggler-icon"></span>
</MudIconButton>
<MudSpacer/>
<MudIconButton Icon="@Icons.Material.Filled.ManageAccounts" Color="Color.Inherit" Edge="Edge.End" @onclick="ToggleProfilMenu"/>
<CultureSelector></CultureSelector>
<MudMenu Icon="@Icons.Material.Filled.AccountCircle" Color="Color.Inherit" Edge="Edge.End">
<MudMenuItem>Profile</MudMenuItem>
<MudButton type="button" class="btn btn-link ml-md-auto" @onclick="@LogoutClick">Logout</MudButton>
</MudMenu>
</MudAppBar>
@* <div class="top-row px-4 auth">
@* <div class="container-fluid toggler-container">
<button title="Navigation menu" class="navbar-toggler custom-toggler" @onclick="ToggleNavMenu">
<span class="navbat-toggler-icon"></span>
</button>
</div> *@
@* Messages, notifs et pp compte à mettre *@
@* <div class="top-row px-4">
<CultureSelector />
<button type="button" class="btn btn-link ml-md-auto" @onclick="@LogoutClick">Logout</button>
</div>
</div> *@
<article class="content px-4">
@Body
</article>
</main>
</div>
@code {
private bool collapseNavMenu = true;
private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null;
private void ToggleNavMenu()
{
collapseNavMenu = !collapseNavMenu;
}
private void ToggleProfilMenu()
{
}
}

@ -1,13 +1,15 @@
using HeartTrack.Services;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components;
using HeartTrack.Services.AuthentificationService;
namespace HeartTrack.Shared
{
public partial class MainLayout
{
/*[Inject]
public CustomStateProvider AuthStateProvider { get; set; }*/
[Inject]
public CustomStateProvider AuthStateProvider { get; set; }
[Inject]
public NavigationManager NavigationManager { get; set; }
@ -15,18 +17,31 @@ namespace HeartTrack.Shared
[CascadingParameter]
private Task<AuthenticationState> AuthenticationState { get; set; }
/*protected override async Task OnParametersSetAsync()
private bool collapseNavMenu = true;
private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null;
protected override async Task OnParametersSetAsync()
{
if (!(await AuthenticationState).User.Identity.IsAuthenticated)
{
NavigationManager.NavigateTo("/login");
}
}*/
}
/*private async Task LogoutClick()
private async Task LogoutClick()
{
await AuthStateProvider.Logout();
NavigationManager.NavigateTo("/login");
}*/
}
private void ToggleNavMenu()
{
collapseNavMenu = !collapseNavMenu;
}
private void ToggleProfilMenu()
{
}
}
}

@ -31,15 +31,15 @@
<span class="oi oi-list-rich" aria-hidden="true"></span> @Localizer["Ban"]
</NavLink>
</div>
@* </AuthorizeView>
<AuthorizeView Roles="admin"> *@
@*</AuthorizeView>*@
<AuthorizeView Roles="admin">
<div class="nav-item px-3">
<NavLink class="nav-link" href="tickets">
<span class="oi oi-plus" aria-hidden="true"></span> @Localizer["Ticket"]
</NavLink>
</div>
@* </AuthorizeView>
<AuthorizeView Roles="owner"> *@
</AuthorizeView>
@*@<AuthorizeView Roles="owner"> *@
<div class="nav-item px-3">
<NavLink class="nav-link" href="admin">
<span class="oi oi-plus" aria-hidden="true"></span> @Localizer["Admin"]

Loading…
Cancel
Save