diff --git a/Hcs.WebApp/Components/Layout/MainLayout.razor b/Hcs.WebApp/Components/Layout/MainLayout.razor index 59ad3cd..1823f3f 100644 --- a/Hcs.WebApp/Components/Layout/MainLayout.razor +++ b/Hcs.WebApp/Components/Layout/MainLayout.razor @@ -23,7 +23,7 @@ - + diff --git a/Hcs.WebApp/Components/Pages/Account/Login.razor b/Hcs.WebApp/Components/Pages/Account/Login.razor index 898038f..c1f6067 100644 --- a/Hcs.WebApp/Components/Pages/Account/Login.razor +++ b/Hcs.WebApp/Components/Pages/Account/Login.razor @@ -20,7 +20,7 @@ - + diff --git a/Hcs.WebApp/Components/Pages/Account/Manage.razor b/Hcs.WebApp/Components/Pages/Account/Manage.razor new file mode 100644 index 0000000..e83291f --- /dev/null +++ b/Hcs.WebApp/Components/Pages/Account/Manage.razor @@ -0,0 +1,96 @@ +@page "/account/manage" + +@using Hcs.WebApp.Services +@using Microsoft.AspNetCore.Authorization + +@attribute [Authorize] + +@inject IdentityService IdentityService + +Профиль + + + + +
+ + + @errorMessage + + + Пароль успешно изменен + + + Смена пароля + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +@code { + sealed class PasswordInputModel + { + public string OldPassword { get; set; } = ""; + + public string NewPassword { get; set; } = ""; + + public string ConfirmNewPassword { get; set; } = ""; + } + + bool hasError; + string? errorMessage; + bool hasSuccess; + + [SupplyParameterFromForm] + PasswordInputModel PasswordInput { get; set; } = new(); + + async Task ChangePassword() + { + hasError = false; + hasSuccess = false; + + try + { + await IdentityService.ChangePassword(PasswordInput.OldPassword, PasswordInput.NewPassword); + + hasSuccess = true; + } + catch (Exception e) + { + hasError = true; + errorMessage = e.Message; + } + } +} diff --git a/Hcs.WebApp/Components/Pages/Account/Register.razor b/Hcs.WebApp/Components/Pages/Account/Register.razor index 0f20561..e1692de 100644 --- a/Hcs.WebApp/Components/Pages/Account/Register.razor +++ b/Hcs.WebApp/Components/Pages/Account/Register.razor @@ -33,7 +33,7 @@ - +
diff --git a/Hcs.WebApp/Controllers/IdentityController.cs b/Hcs.WebApp/Controllers/IdentityController.cs index 6593ff9..0ee2f39 100644 --- a/Hcs.WebApp/Controllers/IdentityController.cs +++ b/Hcs.WebApp/Controllers/IdentityController.cs @@ -1,11 +1,14 @@ using Hcs.WebApp.Data; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Radzen; +using System.Security.Claims; namespace Hcs.WebApp.Controllers { - [Route("identity/[action]")] + [Route("identity/")] + [Authorize] public class IdentityController( IUserStore userStore, UserManager userManager, @@ -16,6 +19,8 @@ namespace Hcs.WebApp.Controllers private readonly SignInManager signInManager = signInManager; [HttpPost] + [Route("register")] + [AllowAnonymous] public async Task Register(string userName, string password, string returnUrl) { var user = Activator.CreateInstance(); @@ -24,7 +29,7 @@ namespace Hcs.WebApp.Controllers var result = await userManager.CreateAsync(user, password); if (!result.Succeeded) { - var error = string.Join(", ", result.Errors.Select(error => error.Description)); + var error = string.Join(", ", result.Errors.Select(x => x.Description)); if (!string.IsNullOrEmpty(returnUrl)) { return Redirect($"/account/register?error={error}&returnUrl={Uri.EscapeDataString(returnUrl)}"); @@ -46,6 +51,8 @@ namespace Hcs.WebApp.Controllers } [HttpPost] + [Route("login")] + [AllowAnonymous] public async Task Login(string userName, string password, string returnUrl) { var result = await signInManager.PasswordSignInAsync(userName, password, false, false); @@ -69,11 +76,29 @@ namespace Hcs.WebApp.Controllers return Redirect(returnUrl); } + [HttpGet] + [Route("logout")] public async Task Logout() { await signInManager.SignOutAsync(); return Redirect("/"); } + + [HttpPost] + [Route("change-password")] + public async Task ChangePassword(string oldPassword, string newPassword) + { + var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); + var user = await userManager.FindByIdAsync(userId); + var result = await userManager.ChangePasswordAsync(user, oldPassword, newPassword); + if (result.Succeeded) + { + return Ok(); + } + + var message = string.Join(", ", result.Errors.Select(x => x.Description)); + return BadRequest(message); + } } } diff --git a/Hcs.WebApp/Program.cs b/Hcs.WebApp/Program.cs index 513086e..f59c6f1 100644 --- a/Hcs.WebApp/Program.cs +++ b/Hcs.WebApp/Program.cs @@ -14,11 +14,13 @@ builder.Services builder.Services.AddControllers(); builder.Services.AddRadzenComponents(); -builder.Services.AddHttpClient("Hcs.WebApp").AddHeaderPropagation(x => x.Headers.Add("Cookie")); +builder.Services.AddHttpClient("WithIdentity").AddHeaderPropagation(x => x.Headers.Add("Cookie")); builder.Services.AddHeaderPropagation(x => x.Headers.Add("Cookie")); builder.Services.AddAuthentication(); builder.Services.AddAuthorization(); +builder.Services.AddScoped(); + var connectionString = builder.Configuration.GetConnectionString("IdentityConnection") ?? throw new InvalidOperationException(" 'IdentityConnection'"); builder.Services.AddDbContext(options => options.UseSqlServer(connectionString)); diff --git a/Hcs.WebApp/Services/IdentityService.cs b/Hcs.WebApp/Services/IdentityService.cs new file mode 100644 index 0000000..42f11f7 --- /dev/null +++ b/Hcs.WebApp/Services/IdentityService.cs @@ -0,0 +1,25 @@ +using Microsoft.AspNetCore.Components; + +namespace Hcs.WebApp.Services +{ + public class IdentityService(NavigationManager navigationManager, IHttpClientFactory factory) + { + private readonly Uri baseUri = new($"{navigationManager.BaseUri}identity/"); + private readonly HttpClient httpClient = factory.CreateClient("WithIdentity"); + + public async Task ChangePassword(string oldPassword, string newPassword) + { + var uri = new Uri($"{baseUri}change-password"); + var content = new FormUrlEncodedContent(new Dictionary { + { "oldPassword", oldPassword }, + { "newPassword", newPassword } + }); + var response = await httpClient.PostAsync(uri, content); + if (!response.IsSuccessStatusCode) + { + var message = await response.Content.ReadAsStringAsync(); + throw new ApplicationException(message); + } + } + } +}