From 42c05099786de77bffc93d2b3ac19e7697e798b9 Mon Sep 17 00:00:00 2001 From: "HOME-LAPTOP\\kshkulev" Date: Sun, 19 Oct 2025 19:09:44 +0900 Subject: [PATCH] Add new user creation --- Hcs.WebApp/Components/Layout/MainLayout.razor | 14 +- .../Components/Pages/Account/Register.razor | 82 ------ .../Components/Pages/Management/AddUser.razor | 132 +++++++++ .../Components/Pages/Management/Users.razor | 112 +++++++ .../Shared/LocalizedRadzenDataGrid.cs | 49 ++++ Hcs.WebApp/Controllers/IdentityController.cs | 34 --- Hcs.WebApp/Data/AppIdentityDbContext.cs | 9 +- Hcs.WebApp/Data/AppRole.cs | 2 + Hcs.WebApp/Data/AppUserWithRole.cs | 9 + ...20251019083804_AddRolePriority.Designer.cs | 277 ++++++++++++++++++ .../20251019083804_AddRolePriority.cs | 28 ++ .../AppIdentityDbContextModelSnapshot.cs | 66 +++-- Hcs.WebApp/Program.cs | 13 +- Hcs.WebApp/Services/UsersService.cs | 40 +++ 14 files changed, 710 insertions(+), 157 deletions(-) delete mode 100644 Hcs.WebApp/Components/Pages/Account/Register.razor create mode 100644 Hcs.WebApp/Components/Pages/Management/AddUser.razor create mode 100644 Hcs.WebApp/Components/Pages/Management/Users.razor create mode 100644 Hcs.WebApp/Components/Shared/LocalizedRadzenDataGrid.cs create mode 100644 Hcs.WebApp/Data/AppUserWithRole.cs create mode 100644 Hcs.WebApp/Data/Migrations/20251019083804_AddRolePriority.Designer.cs create mode 100644 Hcs.WebApp/Data/Migrations/20251019083804_AddRolePriority.cs create mode 100644 Hcs.WebApp/Services/UsersService.cs diff --git a/Hcs.WebApp/Components/Layout/MainLayout.razor b/Hcs.WebApp/Components/Layout/MainLayout.razor index 264fbdb..928580d 100644 --- a/Hcs.WebApp/Components/Layout/MainLayout.razor +++ b/Hcs.WebApp/Components/Layout/MainLayout.razor @@ -17,9 +17,16 @@ - - - + + + + + + + + + + @@ -28,7 +35,6 @@ - diff --git a/Hcs.WebApp/Components/Pages/Account/Register.razor b/Hcs.WebApp/Components/Pages/Account/Register.razor deleted file mode 100644 index e1692de..0000000 --- a/Hcs.WebApp/Components/Pages/Account/Register.razor +++ /dev/null @@ -1,82 +0,0 @@ -@page "/account/register" - -@inject NotificationService NotificationService - -Регистрация аккаунта - - - - - Регистрация - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -@code { - sealed class InputModel - { - public string UserName { get; set; } = ""; - - public string Password { get; set; } = ""; - - public string ConfirmPassword { get; set; } = ""; - } - - [SupplyParameterFromForm] - InputModel Input { get; set; } = new(); - - [SupplyParameterFromQuery] - string? Error { get; set; } - - [SupplyParameterFromQuery] - string? ReturnUrl { get; set; } - - protected override void OnAfterRender(bool firstRender) - { - base.OnAfterRender(firstRender); - - if (firstRender) - { - if (!string.IsNullOrEmpty(Error)) - { - NotificationService.Notify(new NotificationMessage() - { - Severity = NotificationSeverity.Error, - Summary = "Ошибка", - Detail = Error, - Duration = -1d - }); - } - } - } -} diff --git a/Hcs.WebApp/Components/Pages/Management/AddUser.razor b/Hcs.WebApp/Components/Pages/Management/AddUser.razor new file mode 100644 index 0000000..2248f23 --- /dev/null +++ b/Hcs.WebApp/Components/Pages/Management/AddUser.razor @@ -0,0 +1,132 @@ +@page "/management/add-user" + +@using Hcs.WebApp.Services +@using Microsoft.AspNetCore.Authorization +@using Microsoft.AspNetCore.Identity +@using Microsoft.EntityFrameworkCore + +@attribute [Authorize] + +@inject RoleManager RoleManager +@inject UsersService UsersService +@inject DialogService DialogService + +Создание пользователя + + + + + + @errorMessage + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +@code { + sealed class InputModel + { + public string UserName { get; set; } = ""; + + public AppRole Role { get; set; } + + public string Password { get; set; } = ""; + + public string ConfirmPassword { get; set; } = ""; + } + + IEnumerable roles; + bool inProgress; + bool hasError; + string errorMessage; + + [SupplyParameterFromForm] + InputModel Input { get; set; } = new(); + + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync(); + + roles = await RoleManager.Roles.OrderBy(x => x.Priority).ToListAsync(); + + Input.Role = roles.First(); + } + + async Task DoAddUser(InputModel input) + { + if (inProgress) return; + + inProgress = true; + hasError = false; + + try + { + var result = await UsersService.CreateUser(input.UserName, input.Role.Name, input.Password); + if (result.Succeeded) + { + DialogService.Close(true); + } + else + { + hasError = true; + errorMessage = string.Join(", ", result.Errors.Select(x => x.Description)); + } + } + catch (Exception e) + { + hasError = true; + errorMessage = e.Message; + } + finally + { + inProgress = false; + } + } + + void DoClose() + { + if (inProgress) return; + + DialogService.Close(false); + } +} diff --git a/Hcs.WebApp/Components/Pages/Management/Users.razor b/Hcs.WebApp/Components/Pages/Management/Users.razor new file mode 100644 index 0000000..0ae478e --- /dev/null +++ b/Hcs.WebApp/Components/Pages/Management/Users.razor @@ -0,0 +1,112 @@ +@page "/management/users" + +@using Hcs.WebApp.Services +@using Microsoft.AspNetCore.Authorization +@using Microsoft.AspNetCore.Identity +@using Microsoft.EntityFrameworkCore + +@attribute [Authorize] + +@inject AuthenticationStateProvider AuthenticationStateProvider +@inject UsersService UsersService +@inject DialogService DialogService + +Пользователи + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +@code { + IEnumerable usersWithRoles; + bool isLoading; + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + await base.OnAfterRenderAsync(firstRender); + + if (firstRender) + { + isLoading = true; + + StateHasChanged(); + + var state = await AuthenticationStateProvider.GetAuthenticationStateAsync(); + if (state.User.Identity?.IsAuthenticated ?? false) + { + usersWithRoles = await UsersService.GetUsersWithRole(); + } + + isLoading = false; + + StateHasChanged(); + } + } + + async Task AddUser() + { + var success = await DialogService.OpenAsync( + "Создание пользователя", + null, + new DialogOptions() + { + Width = "420px", + ShowClose = false, + CloseDialogOnEsc = false, + CloseDialogOnOverlayClick = false + }); + + if (success) + { + await UpdateGrid(); + } + } + + async Task EditUser(AppUserWithRole userWithRole) + { + // TODO: Implement method + } + + async Task DeleteUser(AppUserWithRole userWithRole) + { + // TODO: Implement method + } + + async Task UpdateGrid() + { + isLoading = true; + + usersWithRoles = await UsersService.GetUsersWithRole(); + + isLoading = false; + } +} diff --git a/Hcs.WebApp/Components/Shared/LocalizedRadzenDataGrid.cs b/Hcs.WebApp/Components/Shared/LocalizedRadzenDataGrid.cs new file mode 100644 index 0000000..20762ed --- /dev/null +++ b/Hcs.WebApp/Components/Shared/LocalizedRadzenDataGrid.cs @@ -0,0 +1,49 @@ +using Radzen.Blazor; + +namespace Hcs.WebApp.Components.Shared +{ + public class LocalizedRadzenDataGrid : RadzenDataGrid + { + public LocalizedRadzenDataGrid() + { + FilterText = "Фильтр"; + EnumFilterSelectText = "Выбрать..."; + EnumNullFilterText = "Нет значения"; + AndOperatorText = "И"; + OrOperatorText = "ИЛИ"; + ApplyFilterText = "Применить"; + ClearFilterText = "Очистить"; + EqualsText = "Равен"; + NotEqualsText = "Не равен"; + LessThanText = "Меньше чем"; + LessThanOrEqualsText = "Меньше чем или равен"; + GreaterThanText = "Больше чем"; + GreaterThanOrEqualsText = "Больше чем или равен"; + EndsWithText = "Заканчивается на"; + ContainsText = "Содержит"; + DoesNotContainText = "Не содержит"; + InText = "В"; + NotInText = "НЕ В"; + StartsWithText = "Начинается с"; + IsNotNullText = "Не пустой"; + IsNullText = "Пустой"; + IsEmptyText = "Пустой текст"; + IsNotEmptyText = "Не пустой текст"; + CustomText = "Пользовательский"; + EmptyText = "Нет записей"; + + PageSizeText = "зап. на стр."; + PagingSummaryFormat = "Страница {0} из {1} ({2} зап.)"; + FirstPageTitle = "Первая страница"; + FirstPageAriaLabel = "На первую страницу"; + PrevPageTitle = "Предыдущая страница"; + PrevPageAriaLabel = "На предыдущую страницу"; + LastPageTitle = "Последняя страница"; + LastPageAriaLabel = "На последнюю страницу"; + NextPageTitle = "Следующая страница"; + NextPageAriaLabel = "На следующую страницу"; + PageTitleFormat = "Страница {0}"; + PageAriaLabelFormat = "На страницу {0}"; + } + } +} diff --git a/Hcs.WebApp/Controllers/IdentityController.cs b/Hcs.WebApp/Controllers/IdentityController.cs index e1bf463..b05e809 100644 --- a/Hcs.WebApp/Controllers/IdentityController.cs +++ b/Hcs.WebApp/Controllers/IdentityController.cs @@ -10,46 +10,12 @@ namespace Hcs.WebApp.Controllers [Route("identity/")] [Authorize] public class IdentityController( - IUserStore userStore, UserManager userManager, SignInManager signInManager) : Controller { - private readonly IUserStore userStore = userStore; private readonly UserManager userManager = userManager; private readonly SignInManager signInManager = signInManager; - [HttpPost] - [Route("register")] - [AllowAnonymous] - public async Task Register(string userName, string password, string returnUrl) - { - var user = Activator.CreateInstance(); - await userStore.SetUserNameAsync(user, userName, CancellationToken.None); - - var result = await userManager.CreateAsync(user, password); - if (!result.Succeeded) - { - var error = string.Join(", ", result.Errors.Select(x => x.Description)); - if (!string.IsNullOrEmpty(returnUrl)) - { - return Redirect($"/account/register?error={error}&returnUrl={Uri.EscapeDataString(returnUrl)}"); - } - else - { - return Redirect($"/account/register?error={error}"); - } - } - - await signInManager.SignInAsync(user, isPersistent: false); - - if (string.IsNullOrEmpty(returnUrl)) - { - return Redirect("/"); - } - - return Redirect(returnUrl); - } - [HttpPost] [Route("login")] [AllowAnonymous] diff --git a/Hcs.WebApp/Data/AppIdentityDbContext.cs b/Hcs.WebApp/Data/AppIdentityDbContext.cs index 40a9795..cda9ca3 100644 --- a/Hcs.WebApp/Data/AppIdentityDbContext.cs +++ b/Hcs.WebApp/Data/AppIdentityDbContext.cs @@ -18,7 +18,8 @@ namespace Hcs.WebApp.Data adminRole = new AppRole() { Name = AppRole.ADMINISTRATOR_TYPE, - NormalizedName = AppRole.ADMINISTRATOR_TYPE.Normalize() + NormalizedName = AppRole.ADMINISTRATOR_TYPE.Normalize(), + Priority = 0 }; context.Set().Add(adminRole); } @@ -29,7 +30,8 @@ namespace Hcs.WebApp.Data context.Set().Add(new AppRole() { Name = AppRole.OPERATOR_TYPE, - NormalizedName = AppRole.OPERATOR_TYPE.Normalize() + NormalizedName = AppRole.OPERATOR_TYPE.Normalize(), + Priority = 10 }); } @@ -39,7 +41,8 @@ namespace Hcs.WebApp.Data context.Set().Add(new AppRole() { Name = AppRole.OBSERVER_TYPE, - NormalizedName = AppRole.OBSERVER_TYPE.Normalize() + NormalizedName = AppRole.OBSERVER_TYPE.Normalize(), + Priority = 100 }); } diff --git a/Hcs.WebApp/Data/AppRole.cs b/Hcs.WebApp/Data/AppRole.cs index e69a37d..83e098a 100644 --- a/Hcs.WebApp/Data/AppRole.cs +++ b/Hcs.WebApp/Data/AppRole.cs @@ -7,5 +7,7 @@ namespace Hcs.WebApp.Data public const string ADMINISTRATOR_TYPE = "Administrator"; public const string OPERATOR_TYPE = "Operator"; public const string OBSERVER_TYPE = "Observer"; + + public int Priority { get; set; } } } diff --git a/Hcs.WebApp/Data/AppUserWithRole.cs b/Hcs.WebApp/Data/AppUserWithRole.cs new file mode 100644 index 0000000..185392e --- /dev/null +++ b/Hcs.WebApp/Data/AppUserWithRole.cs @@ -0,0 +1,9 @@ +namespace Hcs.WebApp.Data +{ + public class AppUserWithRole + { + public AppUser User { get; set; } + + public AppRole Role { get; set; } + } +} diff --git a/Hcs.WebApp/Data/Migrations/20251019083804_AddRolePriority.Designer.cs b/Hcs.WebApp/Data/Migrations/20251019083804_AddRolePriority.Designer.cs new file mode 100644 index 0000000..1b15159 --- /dev/null +++ b/Hcs.WebApp/Data/Migrations/20251019083804_AddRolePriority.Designer.cs @@ -0,0 +1,277 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable +namespace Hcs.WebApp.Data.Migrations +{ + [DbContext(typeof(AppIdentityDbContext))] + [Migration("20251019083804_AddRolePriority")] + partial class AddRolePriority + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.9") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Hcs.WebApp.Data.AppRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Priority") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Hcs.WebApp.Data.AppUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Hcs.WebApp.Data.AppRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Hcs.WebApp.Data.AppUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Hcs.WebApp.Data.AppUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Hcs.WebApp.Data.AppRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Hcs.WebApp.Data.AppUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Hcs.WebApp.Data.AppUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Hcs.WebApp/Data/Migrations/20251019083804_AddRolePriority.cs b/Hcs.WebApp/Data/Migrations/20251019083804_AddRolePriority.cs new file mode 100644 index 0000000..00b2a67 --- /dev/null +++ b/Hcs.WebApp/Data/Migrations/20251019083804_AddRolePriority.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable +namespace Hcs.WebApp.Data.Migrations +{ + /// + public partial class AddRolePriority : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Priority", + table: "AspNetRoles", + type: "int", + nullable: false, + defaultValue: 0); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Priority", + table: "AspNetRoles"); + } + } +} diff --git a/Hcs.WebApp/Data/Migrations/AppIdentityDbContextModelSnapshot.cs b/Hcs.WebApp/Data/Migrations/AppIdentityDbContextModelSnapshot.cs index cc2a88b..0926a33 100644 --- a/Hcs.WebApp/Data/Migrations/AppIdentityDbContextModelSnapshot.cs +++ b/Hcs.WebApp/Data/Migrations/AppIdentityDbContextModelSnapshot.cs @@ -1,4 +1,5 @@ -using Microsoft.EntityFrameworkCore; +// +using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; #nullable disable @@ -11,11 +12,41 @@ namespace Hcs.WebApp.Data.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "8.0.0") + .HasAnnotation("ProductVersion", "9.0.9") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + modelBuilder.Entity("Hcs.WebApp.Data.AppRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Priority") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + }); + modelBuilder.Entity("Hcs.WebApp.Data.AppUser", b => { b.Property("Id") @@ -81,33 +112,6 @@ namespace Hcs.WebApp.Data.Migrations b.ToTable("AspNetUsers", (string)null); }); - modelBuilder.Entity("Hcs.WebApp.Data.AppRole", b => - { - b.Property("Id") - .HasColumnType("nvarchar(450)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex") - .HasFilter("[NormalizedName] IS NOT NULL"); - - b.ToTable("AspNetRoles", (string)null); - }); - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => { b.Property("Id") @@ -216,7 +220,7 @@ namespace Hcs.WebApp.Data.Migrations modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + b.HasOne("Hcs.WebApp.Data.AppRole", null) .WithMany() .HasForeignKey("RoleId") .OnDelete(DeleteBehavior.Cascade) @@ -243,7 +247,7 @@ namespace Hcs.WebApp.Data.Migrations modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + b.HasOne("Hcs.WebApp.Data.AppRole", null) .WithMany() .HasForeignKey("RoleId") .OnDelete(DeleteBehavior.Cascade) diff --git a/Hcs.WebApp/Program.cs b/Hcs.WebApp/Program.cs index f59c6f1..7cb9324 100644 --- a/Hcs.WebApp/Program.cs +++ b/Hcs.WebApp/Program.cs @@ -1,9 +1,12 @@ using Hcs.WebApp.Components; +using Hcs.WebApp.Components.Shared; using Hcs.WebApp.Data; using Hcs.WebApp.Services; +using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Radzen; +using Radzen.Blazor; var builder = WebApplication.CreateBuilder(args); @@ -19,10 +22,8 @@ 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)); +builder.Services.AddDbContextFactory(options => options.UseSqlServer(connectionString)); builder.Services .AddIdentity(options => @@ -42,6 +43,12 @@ builder.Services.AddTransient(); #else builder.Services.AddTransient(); #endif +builder.Services.AddScoped(); +builder.Services.AddScoped(); + +var activator = new RadzenComponentActivator(); +activator.Override(typeof(RadzenDataGrid<>), typeof(LocalizedRadzenDataGrid<>)); +builder.Services.AddSingleton(activator); var app = builder.Build(); diff --git a/Hcs.WebApp/Services/UsersService.cs b/Hcs.WebApp/Services/UsersService.cs new file mode 100644 index 0000000..70bcf79 --- /dev/null +++ b/Hcs.WebApp/Services/UsersService.cs @@ -0,0 +1,40 @@ +using Hcs.WebApp.Data; +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; + +namespace Hcs.WebApp.Services +{ + public class UsersService(IDbContextFactory factory, UserManager userManager) + { + private readonly IDbContextFactory factory = factory; + private readonly UserManager userManager = userManager; + + public async Task> GetUsersWithRole() + { + using var context = factory.CreateDbContext(); + return await (from user in context.Users + join userRole in context.UserRoles on user.Id equals userRole.UserId + join role in context.Roles on userRole.RoleId equals role.Id + select new AppUserWithRole() + { + User = user, + Role = role + }).ToListAsync(); + } + + public async Task CreateUser(string userName, string roleName, string password) + { + var user = new AppUser() + { + UserName = userName, + NormalizedUserName = userName.Normalize() + }; + var result = await userManager.CreateAsync(user, password); + if (result.Succeeded) + { + result = await userManager.AddToRolesAsync(user, [roleName]); + } + return result; + } + } +}