diff --git a/UserManagement.API/Controllers/AuthController.cs b/UserManagement.API/Controllers/AuthController.cs index 8e23370..13fc241 100644 --- a/UserManagement.API/Controllers/AuthController.cs +++ b/UserManagement.API/Controllers/AuthController.cs @@ -1,11 +1,11 @@ -using Microsoft.AspNetCore.Authentication.Cookies; -using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Swashbuckle.AspNetCore.Annotations; using System.Security.Claims; using UserManagement.Application.Dtos.Auth; using UserManagement.Application.Interfaces; -using Swashbuckle.AspNetCore.Annotations; namespace UserManagement.API.Controllers { @@ -30,7 +30,7 @@ namespace UserManagement.API.Controllers { // Validate user var user = await _userService.GetByUsernameAsync(login.Username); - if (user == null) + if (user is null) { return Unauthorized(); } @@ -49,9 +49,13 @@ namespace UserManagement.API.Controllers new Claim(ClaimTypes.Name, user.UserName), new Claim(ClaimTypes.Surname, user.LastName ?? ""), new Claim(ClaimTypes.GivenName, user.FirstName ?? ""), - new Claim(ClaimTypes.Role, user?.Role?.Name.ToString() ?? "") }; + foreach (var userRole in user.UserRoles) + { + claims.Add(new Claim(ClaimTypes.Role, userRole!.Name)); + } + // Create a ClaimsIdentity based on the created claims var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); @@ -82,5 +86,10 @@ namespace UserManagement.API.Controllers return Ok(); } + + // AUTH CHECK + [HttpGet] + [SwaggerOperation(Summary = "Authentication Check")] + public IActionResult IsAuth() => Ok(User?.Identity?.IsAuthenticated ?? false); } } diff --git a/UserManagement.API/Controllers/RoleController.cs b/UserManagement.API/Controllers/RoleController.cs index cddd5ee..bc62d36 100644 --- a/UserManagement.API/Controllers/RoleController.cs +++ b/UserManagement.API/Controllers/RoleController.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Mvc; using Swashbuckle.AspNetCore.Annotations; using UserManagement.Application.Dtos.Incomming; using UserManagement.Application.Interfaces; +using static Microsoft.EntityFrameworkCore.DbLoggerCategory; namespace UserManagement.API.Controllers { @@ -114,7 +115,18 @@ namespace UserManagement.API.Controllers [ProducesResponseType(StatusCodes.Status400BadRequest)] public async Task UpdateRole(int id, UpdatingRoleDto updatingRoleDto) { + if (id <= 0) + { + return BadRequest("Invalid Id"); + } + var updated = await _roleService.UpdateRoleAsync(updatingRoleDto); + + if (!updated) + { + return BadRequest("Update failed"); + } + return Ok(updated); } @@ -126,7 +138,18 @@ namespace UserManagement.API.Controllers [ProducesResponseType(StatusCodes.Status400BadRequest)] public async Task DeleteRole([FromRoute] int id) { - await _roleService.DeleteRoleAsync(id); + if (id <= 0) + { + return BadRequest("Invalid Id"); + } + + var deleted = await _roleService.DeleteRoleAsync(id); + + if (!deleted) + { + return BadRequest("Deletion failed"); + } + return Ok(); } } diff --git a/UserManagement.API/Controllers/UserController.cs b/UserManagement.API/Controllers/UserController.cs index 4bb97bb..9dff221 100644 --- a/UserManagement.API/Controllers/UserController.cs +++ b/UserManagement.API/Controllers/UserController.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Mvc; using Swashbuckle.AspNetCore.Annotations; using UserManagement.Application.Dtos.Incomming; using UserManagement.Application.Interfaces; +using static Microsoft.EntityFrameworkCore.DbLoggerCategory; namespace UserManagement.API.Controllers { @@ -24,6 +25,7 @@ namespace UserManagement.API.Controllers [ProducesResponseType(StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] + //[Authorize(Roles = "Admin")] public async Task CreateUser([FromBody] CreatingUserDto creatingUserDto) { // Validate incomming model @@ -114,7 +116,18 @@ namespace UserManagement.API.Controllers [ProducesResponseType(StatusCodes.Status400BadRequest)] public async Task UpdateUser(int id, UpdatingUserDto updatingUserDto) { + if (id <= 0) + { + return BadRequest("Invalid Id"); + } + var updated = await _userService.UpdateUserAsync(updatingUserDto); + + if (!updated) + { + return BadRequest("Update failed"); + } + return Ok(updated); } @@ -126,7 +139,18 @@ namespace UserManagement.API.Controllers [ProducesResponseType(StatusCodes.Status400BadRequest)] public async Task DeleteUser([FromBody] int id) { - await _userService.DeleteUserAsync(id); + if (id <= 0) + { + return BadRequest("Invalid Id"); + } + + var deleted = await _userService.DeleteUserAsync(id); + + if (!deleted) + { + return BadRequest("Deletion failed"); + } + return Ok(); } } diff --git a/UserManagement.API/Migrations/20240821105311_Initial.Designer.cs b/UserManagement.API/Migrations/20240910064554_Init.Designer.cs similarity index 76% rename from UserManagement.API/Migrations/20240821105311_Initial.Designer.cs rename to UserManagement.API/Migrations/20240910064554_Init.Designer.cs index a2df229..a137f8c 100644 --- a/UserManagement.API/Migrations/20240821105311_Initial.Designer.cs +++ b/UserManagement.API/Migrations/20240910064554_Init.Designer.cs @@ -1,107 +1,133 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using UserManagement.Infrastructure; - -#nullable disable - -namespace UserManagement.API.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20240821105311_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.8") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("UserManagement.Domain.Entities.Role", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasColumnName("ID"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("CreationDate") - .HasColumnType("datetime") - .HasColumnName("CREATION_DATE"); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(max)") - .HasColumnName("ROLE"); - - b.HasKey("Id"); - - b.ToTable("Roles"); - }); - - modelBuilder.Entity("UserManagement.Domain.Entities.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasColumnName("ID"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("FirstName") - .IsRequired() - .HasColumnType("nvarchar(max)") - .HasColumnName("FIRST_NAME"); - - b.Property("LastName") - .IsRequired() - .HasColumnType("nvarchar(max)") - .HasColumnName("LAST_NAME"); - - b.Property("Password") - .IsRequired() - .HasColumnType("nvarchar(max)") - .HasColumnName("PASSWORD"); - - b.Property("ROLE") - .HasColumnType("int"); - - b.Property("RoleId") - .HasColumnType("int") - .HasColumnName("ROLE_ID"); - - b.Property("UserName") - .IsRequired() - .HasColumnType("nvarchar(max)") - .HasColumnName("USER_NAME"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("Users"); - }); - - modelBuilder.Entity("UserManagement.Domain.Entities.User", b => - { - b.HasOne("UserManagement.Domain.Entities.Role", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Role"); - }); -#pragma warning restore 612, 618 - } - } -} +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using UserManagement.Infrastructure; + +#nullable disable + +namespace UserManagement.API.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20240910064554_Init")] + partial class Init + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("UserManagement.Domain.Entities.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("datetime") + .HasColumnName("CREATION_DATE"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ROLE"); + + b.HasKey("Id"); + + b.ToTable("Roles"); + }); + + modelBuilder.Entity("UserManagement.Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("FIRST_NAME"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("LAST_NAME"); + + b.Property("Password") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("PASSWORD"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("USER_NAME"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("UserManagement.Domain.Entities.UserRoles", b => + { + b.Property("UserId") + .HasColumnType("int") + .HasColumnName("USER_ID"); + + b.Property("RoleId") + .HasColumnType("int") + .HasColumnName("ROLE_ID"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("UserRoles"); + }); + + modelBuilder.Entity("UserManagement.Domain.Entities.UserRoles", b => + { + b.HasOne("UserManagement.Domain.Entities.Role", "Role") + .WithMany("UserRoles") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("UserManagement.Domain.Entities.User", "User") + .WithMany("UserRoles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("UserManagement.Domain.Entities.Role", b => + { + b.Navigation("UserRoles"); + }); + + modelBuilder.Entity("UserManagement.Domain.Entities.User", b => + { + b.Navigation("UserRoles"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/UserManagement.API/Migrations/20240821105311_Initial.cs b/UserManagement.API/Migrations/20240910064554_Init.cs similarity index 70% rename from UserManagement.API/Migrations/20240821105311_Initial.cs rename to UserManagement.API/Migrations/20240910064554_Init.cs index b9139a9..be1b7d4 100644 --- a/UserManagement.API/Migrations/20240821105311_Initial.cs +++ b/UserManagement.API/Migrations/20240910064554_Init.cs @@ -1,68 +1,87 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace UserManagement.API.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "Roles", - columns: table => new - { - ID = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - ROLE = table.Column(type: "nvarchar(max)", nullable: false), - CREATION_DATE = table.Column(type: "datetime", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Roles", x => x.ID); - }); - - migrationBuilder.CreateTable( - name: "Users", - columns: table => new - { - ID = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - USER_NAME = table.Column(type: "nvarchar(max)", nullable: false), - FIRST_NAME = table.Column(type: "nvarchar(max)", nullable: false), - LAST_NAME = table.Column(type: "nvarchar(max)", nullable: false), - PASSWORD = table.Column(type: "nvarchar(max)", nullable: false), - ROLE_ID = table.Column(type: "int", nullable: false), - ROLE = table.Column(type: "int", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Users", x => x.ID); - table.ForeignKey( - name: "FK_Users_Roles_ROLE_ID", - column: x => x.ROLE_ID, - principalTable: "Roles", - principalColumn: "ID", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_Users_ROLE_ID", - table: "Users", - column: "ROLE_ID"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Users"); - - migrationBuilder.DropTable( - name: "Roles"); - } - } -} +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace UserManagement.API.Migrations +{ + /// + public partial class Init : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Roles", + columns: table => new + { + ID = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + ROLE = table.Column(type: "nvarchar(max)", nullable: false), + CREATION_DATE = table.Column(type: "datetime", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Roles", x => x.ID); + }); + + migrationBuilder.CreateTable( + name: "Users", + columns: table => new + { + ID = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + USER_NAME = table.Column(type: "nvarchar(max)", nullable: false), + FIRST_NAME = table.Column(type: "nvarchar(max)", nullable: false), + LAST_NAME = table.Column(type: "nvarchar(max)", nullable: false), + PASSWORD = table.Column(type: "nvarchar(max)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Users", x => x.ID); + }); + + migrationBuilder.CreateTable( + name: "UserRoles", + columns: table => new + { + USER_ID = table.Column(type: "int", nullable: false), + ROLE_ID = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_UserRoles", x => new { x.USER_ID, x.ROLE_ID }); + table.ForeignKey( + name: "FK_UserRoles_Roles_ROLE_ID", + column: x => x.ROLE_ID, + principalTable: "Roles", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_UserRoles_Users_USER_ID", + column: x => x.USER_ID, + principalTable: "Users", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_UserRoles_ROLE_ID", + table: "UserRoles", + column: "ROLE_ID"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "UserRoles"); + + migrationBuilder.DropTable( + name: "Roles"); + + migrationBuilder.DropTable( + name: "Users"); + } + } +} diff --git a/UserManagement.API/Migrations/20240910112722_Second.Designer.cs b/UserManagement.API/Migrations/20240910112722_Second.Designer.cs new file mode 100644 index 0000000..ac63104 --- /dev/null +++ b/UserManagement.API/Migrations/20240910112722_Second.Designer.cs @@ -0,0 +1,133 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using UserManagement.Infrastructure; + +#nullable disable + +namespace UserManagement.API.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20240910112722_Second")] + partial class Second + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("UserManagement.Domain.Entities.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("datetime") + .HasColumnName("CREATION_DATE"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("ROLE"); + + b.HasKey("Id"); + + b.ToTable("Roles"); + }); + + modelBuilder.Entity("UserManagement.Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("FIRST_NAME"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("LAST_NAME"); + + b.Property("Password") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("PASSWORD"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("USER_NAME"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("UserManagement.Domain.Entities.UserRole", b => + { + b.Property("UserId") + .HasColumnType("int") + .HasColumnName("USER_ID"); + + b.Property("RoleId") + .HasColumnType("int") + .HasColumnName("ROLE_ID"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("UserRoles"); + }); + + modelBuilder.Entity("UserManagement.Domain.Entities.UserRole", b => + { + b.HasOne("UserManagement.Domain.Entities.Role", "Role") + .WithMany("UserRoles") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("UserManagement.Domain.Entities.User", "User") + .WithMany("UserRoles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("UserManagement.Domain.Entities.Role", b => + { + b.Navigation("UserRoles"); + }); + + modelBuilder.Entity("UserManagement.Domain.Entities.User", b => + { + b.Navigation("UserRoles"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/UserManagement.API/Migrations/20240910112722_Second.cs b/UserManagement.API/Migrations/20240910112722_Second.cs new file mode 100644 index 0000000..1d31bc8 --- /dev/null +++ b/UserManagement.API/Migrations/20240910112722_Second.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace UserManagement.API.Migrations +{ + /// + public partial class Second : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/UserManagement.API/Migrations/20240911113732_UniqueProperties.Designer.cs b/UserManagement.API/Migrations/20240911113732_UniqueProperties.Designer.cs new file mode 100644 index 0000000..21c7724 --- /dev/null +++ b/UserManagement.API/Migrations/20240911113732_UniqueProperties.Designer.cs @@ -0,0 +1,139 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using UserManagement.Infrastructure; + +#nullable disable + +namespace UserManagement.API.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20240911113732_UniqueProperties")] + partial class UniqueProperties + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("UserManagement.Domain.Entities.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("datetime") + .HasColumnName("CREATION_DATE"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(450)") + .HasColumnName("ROLE"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Roles"); + }); + + modelBuilder.Entity("UserManagement.Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("ID"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("FIRST_NAME"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("LAST_NAME"); + + b.Property("Password") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("PASSWORD"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("nvarchar(450)") + .HasColumnName("USER_NAME"); + + b.HasKey("Id"); + + b.HasIndex("UserName") + .IsUnique(); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("UserManagement.Domain.Entities.UserRole", b => + { + b.Property("UserId") + .HasColumnType("int") + .HasColumnName("USER_ID"); + + b.Property("RoleId") + .HasColumnType("int") + .HasColumnName("ROLE_ID"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("UserRoles"); + }); + + modelBuilder.Entity("UserManagement.Domain.Entities.UserRole", b => + { + b.HasOne("UserManagement.Domain.Entities.Role", "Role") + .WithMany("UserRoles") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("UserManagement.Domain.Entities.User", "User") + .WithMany("UserRoles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("UserManagement.Domain.Entities.Role", b => + { + b.Navigation("UserRoles"); + }); + + modelBuilder.Entity("UserManagement.Domain.Entities.User", b => + { + b.Navigation("UserRoles"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/UserManagement.API/Migrations/20240911113732_UniqueProperties.cs b/UserManagement.API/Migrations/20240911113732_UniqueProperties.cs new file mode 100644 index 0000000..5463478 --- /dev/null +++ b/UserManagement.API/Migrations/20240911113732_UniqueProperties.cs @@ -0,0 +1,70 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace UserManagement.API.Migrations +{ + /// + public partial class UniqueProperties : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "USER_NAME", + table: "Users", + type: "nvarchar(450)", + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AlterColumn( + name: "ROLE", + table: "Roles", + type: "nvarchar(450)", + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.CreateIndex( + name: "IX_Users_USER_NAME", + table: "Users", + column: "USER_NAME", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Roles_ROLE", + table: "Roles", + column: "ROLE", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_Users_USER_NAME", + table: "Users"); + + migrationBuilder.DropIndex( + name: "IX_Roles_ROLE", + table: "Roles"); + + migrationBuilder.AlterColumn( + name: "USER_NAME", + table: "Users", + type: "nvarchar(max)", + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(450)"); + + migrationBuilder.AlterColumn( + name: "ROLE", + table: "Roles", + type: "nvarchar(max)", + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(450)"); + } + } +} diff --git a/UserManagement.API/Migrations/ApplicationDbContextModelSnapshot.cs b/UserManagement.API/Migrations/ApplicationDbContextModelSnapshot.cs index 4c1466e..8b7ac90 100644 --- a/UserManagement.API/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/UserManagement.API/Migrations/ApplicationDbContextModelSnapshot.cs @@ -37,11 +37,14 @@ namespace UserManagement.API.Migrations b.Property("Name") .IsRequired() - .HasColumnType("nvarchar(max)") + .HasColumnType("nvarchar(450)") .HasColumnName("ROLE"); b.HasKey("Id"); + b.HasIndex("Name") + .IsUnique(); + b.ToTable("Roles"); }); @@ -69,34 +72,63 @@ namespace UserManagement.API.Migrations .HasColumnType("nvarchar(max)") .HasColumnName("PASSWORD"); - b.Property("ROLE") - .HasColumnType("int"); + b.Property("UserName") + .IsRequired() + .HasColumnType("nvarchar(450)") + .HasColumnName("USER_NAME"); + + b.HasKey("Id"); + + b.HasIndex("UserName") + .IsUnique(); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("UserManagement.Domain.Entities.UserRole", b => + { + b.Property("UserId") + .HasColumnType("int") + .HasColumnName("USER_ID"); b.Property("RoleId") .HasColumnType("int") .HasColumnName("ROLE_ID"); - b.Property("UserName") - .IsRequired() - .HasColumnType("nvarchar(max)") - .HasColumnName("USER_NAME"); - - b.HasKey("Id"); + b.HasKey("UserId", "RoleId"); b.HasIndex("RoleId"); - b.ToTable("Users"); + b.ToTable("UserRoles"); }); - modelBuilder.Entity("UserManagement.Domain.Entities.User", b => + modelBuilder.Entity("UserManagement.Domain.Entities.UserRole", b => { b.HasOne("UserManagement.Domain.Entities.Role", "Role") - .WithMany() + .WithMany("UserRoles") .HasForeignKey("RoleId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); + b.HasOne("UserManagement.Domain.Entities.User", "User") + .WithMany("UserRoles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + b.Navigation("Role"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("UserManagement.Domain.Entities.Role", b => + { + b.Navigation("UserRoles"); + }); + + modelBuilder.Entity("UserManagement.Domain.Entities.User", b => + { + b.Navigation("UserRoles"); }); #pragma warning restore 612, 618 } diff --git a/UserManagement.API/UserManagement.API.csproj b/UserManagement.API/UserManagement.API.csproj index 48a72ef..477cbe2 100644 --- a/UserManagement.API/UserManagement.API.csproj +++ b/UserManagement.API/UserManagement.API.csproj @@ -8,6 +8,7 @@ + diff --git a/UserManagement.API/appsettings.json b/UserManagement.API/appsettings.json index 9c23c60..23cc126 100644 --- a/UserManagement.API/appsettings.json +++ b/UserManagement.API/appsettings.json @@ -1,6 +1,6 @@ { "ConnectionStrings": { - "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=UserManagement;Trusted_Connection=True;TrustServerCertificate=True;" + "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ProjectUserManagement;Trusted_Connection=True;TrustServerCertificate=True;" }, "Logging": { "LogLevel": { diff --git a/UserManagement.App/Dtos/Incomming/CreatingUserDto.cs b/UserManagement.App/Dtos/Incomming/CreatingUserDto.cs index e128f78..9a20497 100644 --- a/UserManagement.App/Dtos/Incomming/CreatingUserDto.cs +++ b/UserManagement.App/Dtos/Incomming/CreatingUserDto.cs @@ -1,4 +1,6 @@ -namespace UserManagement.Application.Dtos.Incomming +using UserManagement.Application.Dtos.Outgoing; + +namespace UserManagement.Application.Dtos.Incomming { public class CreatingUserDto { @@ -10,6 +12,6 @@ public string Password { get; init; } - public int RoleId { get; set; } + public ICollection RoleIds { get; set; } } } diff --git a/UserManagement.App/Dtos/Incomming/UpdatingUserDto.cs b/UserManagement.App/Dtos/Incomming/UpdatingUserDto.cs index 79b6140..7fb0ae3 100644 --- a/UserManagement.App/Dtos/Incomming/UpdatingUserDto.cs +++ b/UserManagement.App/Dtos/Incomming/UpdatingUserDto.cs @@ -12,6 +12,6 @@ public string Password { get; init; } - public int RoleId { get; set; } + public ICollection RoleIds { get; set; } } } diff --git a/UserManagement.App/Dtos/Outgoing/ReadingRoleDto.cs b/UserManagement.App/Dtos/Outgoing/ReadingRoleDto.cs index 6af3c1b..b04c384 100644 --- a/UserManagement.App/Dtos/Outgoing/ReadingRoleDto.cs +++ b/UserManagement.App/Dtos/Outgoing/ReadingRoleDto.cs @@ -4,6 +4,6 @@ { public int Id { get; set; } - public string Name { get; set; } + public required string Name { get; set; } } } diff --git a/UserManagement.App/Dtos/Outgoing/ReadingUserDto.cs b/UserManagement.App/Dtos/Outgoing/ReadingUserDto.cs index cf22adb..1101eb5 100644 --- a/UserManagement.App/Dtos/Outgoing/ReadingUserDto.cs +++ b/UserManagement.App/Dtos/Outgoing/ReadingUserDto.cs @@ -10,6 +10,6 @@ public string LastName { get; set; } - public ReadingRoleDto? Role { get; set; } + public ICollection UserRoles { get; set; } } } diff --git a/UserManagement.App/Dtos/Outgoing/ReadingUserRolesDto.cs b/UserManagement.App/Dtos/Outgoing/ReadingUserRolesDto.cs new file mode 100644 index 0000000..0e7ece4 --- /dev/null +++ b/UserManagement.App/Dtos/Outgoing/ReadingUserRolesDto.cs @@ -0,0 +1,8 @@ +namespace UserManagement.Application.Dtos.Outgoing +{ + public class ReadingUserRolesDto + { + public int RoleId { get; set; } + public string RoleName { get; set; } + } +} diff --git a/UserManagement.App/Interfaces/IUserService.cs b/UserManagement.App/Interfaces/IUserService.cs index 48f771a..e165b97 100644 --- a/UserManagement.App/Interfaces/IUserService.cs +++ b/UserManagement.App/Interfaces/IUserService.cs @@ -21,8 +21,8 @@ namespace UserManagement.Application.Interfaces // UPDATE Task UpdateUserAsync(UpdatingUserDto updatingUserDto); - // UPDATE USER ROLE - Task UpdateUserRoleAsync(int userId, int roleId); + //// UPDATE USER ROLE + //Task UpdateUserRoleAsync(int userId, int roleId); // DELETE Task DeleteUserAsync(int id); diff --git a/UserManagement.App/MappingProfiles/BasicDtoMappingProfile.cs b/UserManagement.App/MappingProfiles/BasicDtoMappingProfile.cs index d349942..41f3331 100644 --- a/UserManagement.App/MappingProfiles/BasicDtoMappingProfile.cs +++ b/UserManagement.App/MappingProfiles/BasicDtoMappingProfile.cs @@ -11,13 +11,43 @@ namespace UserManagement.Application.MappingProfiles { // ROLE CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); // USER - CreateMap().ReverseMap(); - CreateMap().ReverseMap(); - CreateMap().ReverseMap(); + CreateMap() + .ForMember(dest => dest.RoleIds, opt => opt.Ignore()) + .ReverseMap() + .AfterMap((src, dest) => + { + dest.UserRoles = src.RoleIds?.Select(roleId => new UserRole + { + RoleId = roleId, + User = dest + }).ToList(); + }); + + CreateMap() + .ForMember(dest => dest.UserRoles, opt => opt.MapFrom(src => + src.UserRoles.Select(ur => new ReadingRoleDto + { + Id = ur.Role.Id, + Name = ur.Role.Name + }).ToList())); + + CreateMap() + .ForMember(dest => dest.RoleIds, opt => opt.Ignore()) + .ReverseMap() + .AfterMap((src, dest) => + { + dest.UserRoles = src.RoleIds?.Select(roleId => new UserRole + { + RoleId = roleId, + UserId = dest.Id + }).ToList(); + }); } } } diff --git a/UserManagement.App/Services/UserService.cs b/UserManagement.App/Services/UserService.cs index f4df33e..b61ea69 100644 --- a/UserManagement.App/Services/UserService.cs +++ b/UserManagement.App/Services/UserService.cs @@ -23,15 +23,23 @@ namespace UserManagement.Application.Services // CREATE public async Task AddUserAsync(CreatingUserDto creatingUserDto) { - // validating role - var role = await _roleRepository.GetByIdAsync(creatingUserDto.RoleId); - if (role == null) + var user = _mapper.Map(creatingUserDto); + + user.UserRoles = new List(); + + foreach (var roleId in creatingUserDto.RoleIds) { - throw new ArgumentException("Role not found"); + var role = await _roleRepository.GetByIdAsync(roleId); + if (role is not null) + { + user.UserRoles.Add(new UserRole { UserId = user.Id, RoleId = role.Id }); + } + else + { + throw new ArgumentException($"Role with Id {roleId} not found"); + } } - // mapping dto to entity - var user = _mapper.Map(creatingUserDto); var created = await _userRepository.AddAsync(user); return created; } @@ -63,29 +71,52 @@ namespace UserManagement.Application.Services // UPDATE public async Task UpdateUserAsync(UpdatingUserDto updatingUserDto) { - var user = _mapper.Map(updatingUserDto); + var user = await _userRepository.GetByIdAsync(updatingUserDto.Id); + + if (user is null) + { + return false; + } + + _mapper.Map(updatingUserDto, user); + + user.UserRoles.Clear(); + + foreach(var roleId in updatingUserDto.RoleIds) + { + var role = await _roleRepository.GetByIdAsync(roleId); + if (role is not null) + { + user.UserRoles.Add(new UserRole { UserId = user.Id, RoleId = role.Id }); + } + else + { + throw new ArgumentException($"Role with Id {roleId} not found"); + } + } + bool isUpdated = await _userRepository.UpdateAsync(user); return isUpdated; } - // UPDATE USER ROLE -- die Rolle eines Users aktualisieren - public async Task UpdateUserRoleAsync(int userId, int roleId) - { - var user = await _userRepository.GetByIdAsync(userId); - if (user == null) - { - throw new ArgumentException("User not found"); - } + //// UPDATE USER ROLE -- die Rolle eines Users aktualisieren + //public async Task UpdateUserRoleAsync(int userId, int roleId) + //{ + // var user = await _userRepository.GetByIdAsync(userId); + // if (user == null) + // { + // throw new ArgumentException("User not found"); + // } - var role = await _roleRepository.GetByIdAsync(roleId); - if (role == null) - { - throw new ArgumentException("Role not found"); - } + // var role = await _roleRepository.GetByIdAsync(roleId); + // if (role == null) + // { + // throw new ArgumentException("Role not found"); + // } - user.RoleId = roleId; - await _userRepository.SaveAsync(); - } + // user.UserRoles = roleId; + // await _userRepository.SaveAsync(); + //} // DELETE public async Task DeleteUserAsync(int id) diff --git a/UserManagement.App/UserManagement.Application.csproj b/UserManagement.App/UserManagement.Application.csproj index ef626a0..4ce4677 100644 --- a/UserManagement.App/UserManagement.Application.csproj +++ b/UserManagement.App/UserManagement.Application.csproj @@ -8,6 +8,7 @@ + diff --git a/UserManagement.Domain/Entities/Role.cs b/UserManagement.Domain/Entities/Role.cs index b0f9217..ffabd48 100644 --- a/UserManagement.Domain/Entities/Role.cs +++ b/UserManagement.Domain/Entities/Role.cs @@ -1,4 +1,5 @@ -using System.ComponentModel.DataAnnotations; +using System.Collections.ObjectModel; +using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace UserManagement.Domain.Entities @@ -12,7 +13,10 @@ namespace UserManagement.Domain.Entities [Required] [Column("ROLE")] - public string Name { get; set; } + public required string Name { get; set; } + + [Column("USERS")] + public ICollection? UserRoles { get; init; } = new Collection(); [Required] [Column("CREATION_DATE", TypeName = "datetime")] diff --git a/UserManagement.Domain/Entities/User.cs b/UserManagement.Domain/Entities/User.cs index 30cea47..0f053fa 100644 --- a/UserManagement.Domain/Entities/User.cs +++ b/UserManagement.Domain/Entities/User.cs @@ -1,4 +1,5 @@ -using System.ComponentModel.DataAnnotations; +using System.Collections.ObjectModel; +using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace UserManagement.Domain.Entities @@ -22,14 +23,14 @@ namespace UserManagement.Domain.Entities [Column("LAST_NAME")] public string LastName { get; set; } + //[Obsolete("Use password hash")] [Required] [Column("PASSWORD")] - public string Password { get; init; } + public required string Password { get; init; } - [Column("ROLE_ID")] - public int RoleId { get; set; } + //public required string PasswordHash { get; init; } - [ForeignKey("ROLE")] - public Role? Role { get; set; } + [Column("ROLES")] + public ICollection? UserRoles { get; set; } = new Collection(); } } diff --git a/UserManagement.Domain/Entities/UserRole.cs b/UserManagement.Domain/Entities/UserRole.cs new file mode 100644 index 0000000..5d0eac4 --- /dev/null +++ b/UserManagement.Domain/Entities/UserRole.cs @@ -0,0 +1,19 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace UserManagement.Domain.Entities +{ + public class UserRole + { + [Column("USER_ID")] + public int UserId { get; set; } + + [ForeignKey("UserId")] + public User? User { get; set; } + + [Column("ROLE_ID")] + public int RoleId { get; set; } + + [ForeignKey("RoleId")] + public Role? Role { get; set; } + } +} diff --git a/UserManagement.Domain/UserManagement.Domain.csproj b/UserManagement.Domain/UserManagement.Domain.csproj index b4501b1..f3a32e8 100644 --- a/UserManagement.Domain/UserManagement.Domain.csproj +++ b/UserManagement.Domain/UserManagement.Domain.csproj @@ -8,6 +8,7 @@ + diff --git a/UserManagement.Infrastructure/ApplicationDbContext.cs b/UserManagement.Infrastructure/ApplicationDbContext.cs index dd32e7e..012e70b 100644 --- a/UserManagement.Infrastructure/ApplicationDbContext.cs +++ b/UserManagement.Infrastructure/ApplicationDbContext.cs @@ -12,15 +12,32 @@ namespace UserManagement.Infrastructure public DbSet Users { get; set; } public DbSet Roles { get; set; } + public DbSet UserRoles { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Entity() - .HasOne(u => u.Role) - .WithMany() - .HasForeignKey(u => u.RoleId); + .HasIndex(u => u.UserName) + .IsUnique(); + + modelBuilder.Entity() + .HasIndex(r => r.Name) + .IsUnique(); + + modelBuilder.Entity() + .HasKey(ur => new { ur.UserId, ur.RoleId }); + + modelBuilder.Entity() + .HasOne(ur => ur.User) + .WithMany(u => u.UserRoles) + .HasForeignKey(ur => ur.UserId); + + modelBuilder.Entity() + .HasOne(ur => ur.Role) + .WithMany(r => r.UserRoles) + .HasForeignKey(ur => ur.RoleId); } } } diff --git a/UserManagement.Infrastructure/Repositories/UserRepository.cs b/UserManagement.Infrastructure/Repositories/UserRepository.cs index 4968936..8f59032 100644 --- a/UserManagement.Infrastructure/Repositories/UserRepository.cs +++ b/UserManagement.Infrastructure/Repositories/UserRepository.cs @@ -24,19 +24,28 @@ namespace UserManagement.Infrastructure.Repositories // READ ALL public async Task> GetAllAsync() { - return await _context.Users.Include(u => u.Role).ToListAsync(); + return await _context.Users + .Include(u => u.UserRoles) + .ThenInclude(ur => ur.Role) + .ToListAsync(); } // READ BY ID public async Task GetByIdAsync(int id) { - return await _context.Users.Where(user => user.Id == id).Include(user => user.Role).FirstAsync(); + return await _context.Users.Where(user => user.Id == id) + .Include(user => user.UserRoles) + .ThenInclude(ur =>ur.Role) + .FirstAsync(); } // READ BY USERNAME public async Task GetByUsernameAsync(string username) { - return await _context.Users.Include(user => user.Role).FirstOrDefaultAsync(u => u.UserName == username); + return await _context.Users + .Include(user => user.UserRoles) + .ThenInclude(ur => ur.Role) + .FirstOrDefaultAsync(u => u.UserName == username); } // UPDATE diff --git a/UserManagement.Infrastructure/UserManagement.Infrastructure.csproj b/UserManagement.Infrastructure/UserManagement.Infrastructure.csproj index ba469d3..f1ac033 100644 --- a/UserManagement.Infrastructure/UserManagement.Infrastructure.csproj +++ b/UserManagement.Infrastructure/UserManagement.Infrastructure.csproj @@ -8,6 +8,7 @@ +