diff --git a/UserManagement.API/Migrations/20240912141329_PasswordHash.Designer.cs b/UserManagement.API/Migrations/20240912141329_PasswordHash.Designer.cs new file mode 100644 index 0000000..d49de38 --- /dev/null +++ b/UserManagement.API/Migrations/20240912141329_PasswordHash.Designer.cs @@ -0,0 +1,144 @@ +// +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("20240912141329_PasswordHash")] + partial class PasswordHash + { + /// + 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("PasswordHash") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("PASSWORD_HASH"); + + 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/20240912141329_PasswordHash.cs b/UserManagement.API/Migrations/20240912141329_PasswordHash.cs new file mode 100644 index 0000000..3a372d1 --- /dev/null +++ b/UserManagement.API/Migrations/20240912141329_PasswordHash.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace UserManagement.API.Migrations +{ + /// + public partial class PasswordHash : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "PASSWORD_HASH", + table: "Users", + type: "nvarchar(max)", + nullable: false, + defaultValue: ""); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "PASSWORD_HASH", + table: "Users"); + } + } +} diff --git a/UserManagement.API/Migrations/20240912143537_RemoveObsoleteFromPassword.Designer.cs b/UserManagement.API/Migrations/20240912143537_RemoveObsoleteFromPassword.Designer.cs new file mode 100644 index 0000000..4ce0552 --- /dev/null +++ b/UserManagement.API/Migrations/20240912143537_RemoveObsoleteFromPassword.Designer.cs @@ -0,0 +1,144 @@ +// +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("20240912143537_RemoveObsoleteFromPassword")] + partial class RemoveObsoleteFromPassword + { + /// + 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("PasswordHash") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("PASSWORD_HASH"); + + 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/20240912143537_RemoveObsoleteFromPassword.cs b/UserManagement.API/Migrations/20240912143537_RemoveObsoleteFromPassword.cs new file mode 100644 index 0000000..57bcd6f --- /dev/null +++ b/UserManagement.API/Migrations/20240912143537_RemoveObsoleteFromPassword.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace UserManagement.API.Migrations +{ + /// + public partial class RemoveObsoleteFromPassword : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/UserManagement.API/Migrations/20240913081538_RemovePasswordFromUser.Designer.cs b/UserManagement.API/Migrations/20240913081538_RemovePasswordFromUser.Designer.cs new file mode 100644 index 0000000..8af5c0f --- /dev/null +++ b/UserManagement.API/Migrations/20240913081538_RemovePasswordFromUser.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("20240913081538_RemovePasswordFromUser")] + partial class RemovePasswordFromUser + { + /// + 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("PasswordHash") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("PASSWORD_HASH"); + + 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/20240913081538_RemovePasswordFromUser.cs b/UserManagement.API/Migrations/20240913081538_RemovePasswordFromUser.cs new file mode 100644 index 0000000..c6563e3 --- /dev/null +++ b/UserManagement.API/Migrations/20240913081538_RemovePasswordFromUser.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace UserManagement.API.Migrations +{ + /// + public partial class RemovePasswordFromUser : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "PASSWORD", + table: "Users"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "PASSWORD", + table: "Users", + type: "nvarchar(max)", + nullable: false, + defaultValue: ""); + } + } +} diff --git a/UserManagement.API/Migrations/ApplicationDbContextModelSnapshot.cs b/UserManagement.API/Migrations/ApplicationDbContextModelSnapshot.cs index 8b7ac90..5577e8c 100644 --- a/UserManagement.API/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/UserManagement.API/Migrations/ApplicationDbContextModelSnapshot.cs @@ -67,10 +67,10 @@ namespace UserManagement.API.Migrations .HasColumnType("nvarchar(max)") .HasColumnName("LAST_NAME"); - b.Property("Password") + b.Property("PasswordHash") .IsRequired() .HasColumnType("nvarchar(max)") - .HasColumnName("PASSWORD"); + .HasColumnName("PASSWORD_HASH"); b.Property("UserName") .IsRequired() diff --git a/UserManagement.App/Dtos/Incomming/CreatingUserDto.cs b/UserManagement.App/Dtos/Incomming/CreatingUserDto.cs index 9a20497..dd9ab57 100644 --- a/UserManagement.App/Dtos/Incomming/CreatingUserDto.cs +++ b/UserManagement.App/Dtos/Incomming/CreatingUserDto.cs @@ -10,7 +10,7 @@ namespace UserManagement.Application.Dtos.Incomming public string LastName { get; set; } - public string Password { get; init; } + public string Password { get; set; } public ICollection RoleIds { get; set; } } diff --git a/UserManagement.App/Dtos/Incomming/UpdatingUserDto.cs b/UserManagement.App/Dtos/Incomming/UpdatingUserDto.cs index 7fb0ae3..ec6aa2e 100644 --- a/UserManagement.App/Dtos/Incomming/UpdatingUserDto.cs +++ b/UserManagement.App/Dtos/Incomming/UpdatingUserDto.cs @@ -10,7 +10,7 @@ public string LastName { get; set; } - public string Password { get; init; } + public string Password { get; set; } public ICollection RoleIds { get; set; } } diff --git a/UserManagement.App/MappingProfiles/BasicDtoMappingProfile.cs b/UserManagement.App/MappingProfiles/BasicDtoMappingProfile.cs index 41f3331..a4b6656 100644 --- a/UserManagement.App/MappingProfiles/BasicDtoMappingProfile.cs +++ b/UserManagement.App/MappingProfiles/BasicDtoMappingProfile.cs @@ -27,6 +27,8 @@ namespace UserManagement.Application.MappingProfiles RoleId = roleId, User = dest }).ToList(); + + dest.PasswordHash = BCrypt.Net.BCrypt.HashPassword(src.Password); }); CreateMap() @@ -47,6 +49,8 @@ namespace UserManagement.Application.MappingProfiles RoleId = roleId, UserId = dest.Id }).ToList(); + + dest.PasswordHash = BCrypt.Net.BCrypt.HashPassword(src.Password); }); } } diff --git a/UserManagement.App/Services/AuthService.cs b/UserManagement.App/Services/AuthService.cs index 5cefdcf..ba43155 100644 --- a/UserManagement.App/Services/AuthService.cs +++ b/UserManagement.App/Services/AuthService.cs @@ -17,7 +17,7 @@ namespace UserManagement.Application.Services { var user = await _userRepository.GetByUsernameAsync(username); - return user?.Password == password; + return BCrypt.Net.BCrypt.Verify(password, user.PasswordHash); } } } diff --git a/UserManagement.App/Services/UserService.cs b/UserManagement.App/Services/UserService.cs index b61ea69..53bc7d1 100644 --- a/UserManagement.App/Services/UserService.cs +++ b/UserManagement.App/Services/UserService.cs @@ -25,6 +25,11 @@ namespace UserManagement.Application.Services { var user = _mapper.Map(creatingUserDto); + if (!string.IsNullOrEmpty(creatingUserDto.Password)) + { + user.PasswordHash = BCrypt.Net.BCrypt.HashPassword(creatingUserDto.Password); + } + user.UserRoles = new List(); foreach (var roleId in creatingUserDto.RoleIds) @@ -80,6 +85,11 @@ namespace UserManagement.Application.Services _mapper.Map(updatingUserDto, user); + if (!string.IsNullOrEmpty(updatingUserDto.Password)) + { + user.PasswordHash = BCrypt.Net.BCrypt.HashPassword(updatingUserDto.Password); + } + user.UserRoles.Clear(); foreach(var roleId in updatingUserDto.RoleIds) diff --git a/UserManagement.Domain/Entities/User.cs b/UserManagement.Domain/Entities/User.cs index 0f053fa..b259964 100644 --- a/UserManagement.Domain/Entities/User.cs +++ b/UserManagement.Domain/Entities/User.cs @@ -24,11 +24,13 @@ namespace UserManagement.Domain.Entities public string LastName { get; set; } //[Obsolete("Use password hash")] - [Required] - [Column("PASSWORD")] - public required string Password { get; init; } + //[Required] + //[Column("PASSWORD")] + //public required string Password { get; set; } - //public required string PasswordHash { get; init; } + [Required] + [Column("PASSWORD_HASH")] + public required string PasswordHash { get; set; } [Column("ROLES")] public ICollection? UserRoles { get; set; } = new Collection();