diff --git a/src/CraftSharp/App.razor b/src/CraftSharp/App.razor index 64264c7..0ae85f7 100644 --- a/src/CraftSharp/App.razor +++ b/src/CraftSharp/App.razor @@ -1,14 +1,15 @@  - + - Not found - -

Sorry, there's nothing at this address.

+ + +

Sorry, there's nothing at this address.

+
\ No newline at end of file diff --git a/src/CraftSharp/Models/AppUser.cs b/src/CraftSharp/Models/AppUser.cs new file mode 100644 index 0000000..02a6335 --- /dev/null +++ b/src/CraftSharp/Models/AppUser.cs @@ -0,0 +1,9 @@ +namespace CraftSharp.Models +{ + public class AppUser + { + public string Password { get; set; } + public List Roles { get; set; } + public string UserName { get; set; } + } +} diff --git a/src/CraftSharp/Models/ConnexionModel.cs b/src/CraftSharp/Models/ConnexionModel.cs index 0979c90..91115fc 100644 --- a/src/CraftSharp/Models/ConnexionModel.cs +++ b/src/CraftSharp/Models/ConnexionModel.cs @@ -5,11 +5,11 @@ namespace CraftSharp.Models public class ConnexionModel { [Required(ErrorMessage = "Le pseudo est obligatoire.")] - [StringLength(50, ErrorMessage = "Le pseudo est trop long")] - public string? Name { get; set; } + [MinLength(4, ErrorMessage = "Le pseudo est trop court")] + public string? UserName { get; set; } [Required(ErrorMessage = "Le mot de passe est obligatoire.")] - [StringLength(50, ErrorMessage = "Le mot de passe est trop long")] + [MinLength(6, ErrorMessage = "Le mot de passe est trop court")] public string? Password { get; set; } } } diff --git a/src/CraftSharp/Models/CurrentUser.cs b/src/CraftSharp/Models/CurrentUser.cs new file mode 100644 index 0000000..dcb8d61 --- /dev/null +++ b/src/CraftSharp/Models/CurrentUser.cs @@ -0,0 +1,9 @@ +namespace CraftSharp.Models +{ + public class CurrentUser + { + public Dictionary Claims { get; set; } + public bool IsAuthenticated { get; set; } + public string UserName { get; set; } + } +} diff --git a/src/CraftSharp/Models/InscriptionModel.cs b/src/CraftSharp/Models/InscriptionModel.cs index 30c618d..561be78 100644 --- a/src/CraftSharp/Models/InscriptionModel.cs +++ b/src/CraftSharp/Models/InscriptionModel.cs @@ -6,11 +6,10 @@ namespace CraftSharp.Models { [Required(ErrorMessage = "Le pseudo est obligatoire.")] - [StringLength(50, ErrorMessage = "Le pseudo est trop long")] - public string? Pseudo { get; set; } + [MinLength(4, ErrorMessage = "Le pseudo est trop long")] + public string? UserName { get; set; } [Required(ErrorMessage = "L'email est obligatoire.")] - [StringLength(50, ErrorMessage = "Le nom ne doit pas dépasser 50 caractères.")] [RegularExpression(@"^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$", ErrorMessage = "Le format de l'email n'est pas correcte.")] public string? Email { get; set; } @@ -19,7 +18,7 @@ namespace CraftSharp.Models public string? Password { get; set; } [Required(ErrorMessage = "Vous devez confirmer votre mot de passe")] - [StringLength(50, ErrorMessage = "Le pseudo est trop long")] - public string? ConfirmPasswd { get; set; } + [Compare(nameof(Password), ErrorMessage = "Les mot de passe ne correspondent pas!")] + public string? PasswordConfirm { get; set; } } } \ No newline at end of file diff --git a/src/CraftSharp/Pages/Connexion.razor b/src/CraftSharp/Pages/Connexion.razor index 0c41710..8860eb4 100644 --- a/src/CraftSharp/Pages/Connexion.razor +++ b/src/CraftSharp/Pages/Connexion.razor @@ -5,28 +5,27 @@

Connexion

- - - + + + + + +
+ + + + +
+ + +
+ + +
Creer un compte
+
+
diff --git a/src/CraftSharp/Pages/Connexion.razor.cs b/src/CraftSharp/Pages/Connexion.razor.cs index 266d819..ee559df 100644 --- a/src/CraftSharp/Pages/Connexion.razor.cs +++ b/src/CraftSharp/Pages/Connexion.razor.cs @@ -4,21 +4,32 @@ using System.ComponentModel.DataAnnotations; using Microsoft.Extensions.Logging; using Microsoft.AspNetCore.Components; using CraftSharp.Models; +using CraftSharp.Services; namespace CraftSharp.Pages { public partial class Connexion { - private readonly ConnexionModel connexion = new(); + [Inject] + public CustomStateProvider AuthStateProvider { get; set; } - private string _connexionId = "test"; - private string _connexionPasswrd = "test"; + [Inject] + public NavigationManager NavigationManager { get; set; } - private void seConnecter() + private string error { get; set; } + private ConnexionModel loginRequest { get; set; } = new ConnexionModel(); + + private async Task OnSubmit() { - if (connexion.Name == _connexionId && connexion.Password == _connexionPasswrd) + error = null; + try + { + await AuthStateProvider.Login(loginRequest); + NavigationManager.NavigateTo(""); + } + catch (Exception ex) { - NavManager.NavigateTo("/counter"); + error = ex.Message; } } } diff --git a/src/CraftSharp/Pages/Inscription.razor b/src/CraftSharp/Pages/Inscription.razor index 1d93e71..89f3a6c 100644 --- a/src/CraftSharp/Pages/Inscription.razor +++ b/src/CraftSharp/Pages/Inscription.razor @@ -5,40 +5,31 @@

Inscription

- - - + + + + + +
+ + + + +
+ + + + +
+ + + + +
Vous avez un compte ? Connectez vous
+
+
diff --git a/src/CraftSharp/Pages/Inscription.razor.cs b/src/CraftSharp/Pages/Inscription.razor.cs index 1530f25..7136520 100644 --- a/src/CraftSharp/Pages/Inscription.razor.cs +++ b/src/CraftSharp/Pages/Inscription.razor.cs @@ -4,15 +4,33 @@ using System.ComponentModel.DataAnnotations; using Microsoft.Extensions.Logging; using Microsoft.AspNetCore.Components; using CraftSharp.Models; +using CraftSharp.Services; namespace CraftSharp.Pages { public partial class Inscription { - private readonly InscriptionModel inscription = new(); + [Inject] + public CustomStateProvider AuthStateProvider { get; set; } - private void inscrire() + [Inject] + public NavigationManager NavigationManager { get; set; } + + private string error { get; set; } + private InscriptionModel registerRequest { get; set; } = new InscriptionModel(); + + private async Task OnSubmit() { + error = null; + try + { + await AuthStateProvider.Register(registerRequest); + NavigationManager.NavigateTo(""); + } + catch (Exception ex) + { + error = ex.Message; + } } } } diff --git a/src/CraftSharp/Program.cs b/src/CraftSharp/Program.cs index ee51ed3..a6a4f59 100644 --- a/src/CraftSharp/Program.cs +++ b/src/CraftSharp/Program.cs @@ -3,67 +3,73 @@ using Blazorise; using Blazored.Modal; using Blazorise.Bootstrap; using Blazorise.Icons.FontAwesome; -using CraftSharp.Data; +using CraftSharp.Data; using CraftSharp.Services; -using Microsoft.AspNetCore.Components; -using Microsoft.AspNetCore.Components.Web; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Localization; using Microsoft.Extensions.Options; using System.Globalization; +using Microsoft.AspNetCore.Components.Authorization; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +builder.Services.AddRazorPages(); +builder.Services.AddServerSideBlazor(); +builder.Services.AddSingleton(); +builder.Services.AddOptions(); +builder.Services.AddAuthorizationCore(); +builder.Services.AddScoped(); +builder.Services.AddScoped(s => s.GetRequiredService()); +builder.Services.AddScoped(); + +// Add the controller of the app +builder.Services.AddControllers(); + +// Add the localization to the app and specify the resources path +builder.Services.AddLocalization(opts => { opts.ResourcesPath = "Resources"; }); -var builder = WebApplication.CreateBuilder(args); - -// Add services to the container. -builder.Services.AddRazorPages(); -builder.Services.AddServerSideBlazor(); -builder.Services.AddSingleton(); - -// Add the controller of the app -builder.Services.AddControllers(); - -// Add the localization to the app and specify the resources path -builder.Services.AddLocalization(opts => { opts.ResourcesPath = "Resources"; }); - builder.Services.AddHttpClient(); - + builder.Services.AddBlazoredModal(); builder.Services .AddBlazorise() .AddBootstrapProviders() - .AddFontAwesomeIcons(); - -builder.Services.AddBlazoredLocalStorage(); - -builder.Services.AddScoped(); - -// Configure the localtization -builder.Services.Configure(options => -{ - // Set the default culture of the web site + .AddFontAwesomeIcons(); + +builder.Services.AddBlazoredLocalStorage(); + +builder.Services.AddScoped(); + +// Configure the localtization +builder.Services.Configure(options => +{ + // Set the default culture of the web site options.DefaultRequestCulture = new RequestCulture(new CultureInfo("en-US")); // Declare the supported culture - options.SupportedCultures = new List { new CultureInfo("en-US"), new CultureInfo("fr-FR"), new CultureInfo("tr-TR") }; - options.SupportedUICultures = new List { new CultureInfo("en-US"), new CultureInfo("fr-FR"), new CultureInfo("tr-TR") }; -}); - -var app = builder.Build(); - -// Configure the HTTP request pipeline. -if (!app.Environment.IsDevelopment()) -{ - app.UseExceptionHandler("/Error"); - // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. - app.UseHsts(); -} - -app.UseHttpsRedirection(); - -app.UseStaticFiles(); - -app.UseRouting(); - + options.SupportedCultures = new List { new CultureInfo("en-US"), new CultureInfo("fr-FR"), new CultureInfo("tr-TR") }; + options.SupportedUICultures = new List { new CultureInfo("en-US"), new CultureInfo("fr-FR"), new CultureInfo("tr-TR") }; +}); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (!app.Environment.IsDevelopment()) +{ + app.UseExceptionHandler("/Error"); + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + app.UseHsts(); +} + +app.UseHttpsRedirection(); + +app.UseStaticFiles(); + +app.UseRouting(); + // Get the current localization options var options = ((IApplicationBuilder)app).ApplicationServices.GetService>(); @@ -77,9 +83,9 @@ if (options?.Value != null) app.UseEndpoints(endpoints => { endpoints.MapControllers(); -}); - -app.MapBlazorHub(); -app.MapFallbackToPage("/_Host"); - -app.Run(); +}); + +app.MapBlazorHub(); +app.MapFallbackToPage("/_Host"); + +app.Run(); diff --git a/src/CraftSharp/Services/AuthService.cs b/src/CraftSharp/Services/AuthService.cs new file mode 100644 index 0000000..f43b69d --- /dev/null +++ b/src/CraftSharp/Services/AuthService.cs @@ -0,0 +1,53 @@ +using CraftSharp.Models; +using System.Security.Claims; + +namespace CraftSharp.Services +{ + public class AuthService : IAuthService + { + private static readonly List CurrentUser; + + static AuthService() + { + CurrentUser = new List + { + new AppUser { UserName = "Admin", Password = "123456", Roles = new List { "admin" } } + }; + } + + public CurrentUser GetUser(string userName) + { + var user = CurrentUser.FirstOrDefault(w => w.UserName == userName); + + if (user == null) + { + throw new Exception("User name or password invalid !"); + } + + var claims = new List(); + claims.AddRange(user.Roles.Select(s => new Claim(ClaimTypes.Role, s))); + + return new CurrentUser + { + IsAuthenticated = true, + UserName = user.UserName, + Claims = claims.ToDictionary(c => c.Type, c => c.Value) + }; + } + + public void Login(ConnexionModel loginRequest) + { + var user = CurrentUser.FirstOrDefault(w => w.UserName == loginRequest.UserName && w.Password == loginRequest.Password); + + if (user == null) + { + throw new Exception("User name or password invalid !"); + } + } + + public void Register(InscriptionModel registerRequest) + { + CurrentUser.Add(new AppUser { UserName = registerRequest.UserName, Password = registerRequest.Password, Roles = new List { "guest" } }); + } + } +} diff --git a/src/CraftSharp/Services/CustomStateProvider.cs b/src/CraftSharp/Services/CustomStateProvider.cs new file mode 100644 index 0000000..7270a67 --- /dev/null +++ b/src/CraftSharp/Services/CustomStateProvider.cs @@ -0,0 +1,75 @@ +using CraftSharp.Models; +using Microsoft.AspNetCore.Components.Authorization; +using System.Security.Claims; + +namespace CraftSharp.Services +{ + public class CustomStateProvider : AuthenticationStateProvider + { + private readonly IAuthService _authService; + private CurrentUser _currentUser; + + public CustomStateProvider(IAuthService authService) + { + this._authService = authService; + } + + public override async Task GetAuthenticationStateAsync() + { + var identity = new ClaimsIdentity(); + try + { + var userInfo = GetCurrentUser(); + if (userInfo.IsAuthenticated) + { + 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"); + } + } + catch (HttpRequestException ex) + { + Console.WriteLine("Request failed:" + ex); + } + + return new AuthenticationState(new ClaimsPrincipal(identity)); + } + + public async Task Login(ConnexionModel loginParameters) + { + _authService.Login(loginParameters); + + // No error - Login the user + var user = _authService.GetUser(loginParameters.UserName); + _currentUser = user; + + NotifyAuthenticationStateChanged(GetAuthenticationStateAsync()); + } + + public async Task Logout() + { + _currentUser = null; + NotifyAuthenticationStateChanged(GetAuthenticationStateAsync()); + } + + public async Task Register(InscriptionModel registerParameters) + { + _authService.Register(registerParameters); + + // No error - Login the user + var user = _authService.GetUser(registerParameters.UserName); + _currentUser = user; + + NotifyAuthenticationStateChanged(GetAuthenticationStateAsync()); + } + + private CurrentUser GetCurrentUser() + { + if (_currentUser != null && _currentUser.IsAuthenticated) + { + return _currentUser; + } + + return new CurrentUser(); + } + } +} diff --git a/src/CraftSharp/Services/IAuthService.cs b/src/CraftSharp/Services/IAuthService.cs new file mode 100644 index 0000000..255eb81 --- /dev/null +++ b/src/CraftSharp/Services/IAuthService.cs @@ -0,0 +1,13 @@ +using CraftSharp.Models; + +namespace CraftSharp.Services +{ + public interface IAuthService + { + CurrentUser GetUser(string userName); + + void Login(ConnexionModel loginRequest); + + void Register(InscriptionModel registerRequest); + } +}