Commit 45a07430 authored by Almouhannad's avatar Almouhannad

(B) Add password hasher, seed user roles (and fix id bug in role)

parent 00408ab3
using Domain.Entities.Medicals.Medicines.MedicineFormValues;
using Domain.Entities.Identity.UserRoles;
using Domain.Entities.Medicals.Medicines.MedicineFormValues;
using Domain.Entities.People.Doctors.Shared.Constants.DoctorStatusValues;
using Domain.Entities.People.Employees.Relations.EmployeeFamilyMembers.FamilyRoleValues;
using Domain.Entities.People.Shared.GenderValues;
......@@ -23,6 +24,9 @@ public class SeedHelper
var seedMedicineForms = serviceScope.ServiceProvider.GetRequiredService<ISeed<MedicineForm>>();
await seedMedicineForms.Seed();
var seedUserRoles = serviceScope.ServiceProvider.GetRequiredService<ISeed<Role>>();
await seedUserRoles.Seed();
}
}
}
......@@ -10,6 +10,8 @@ public class RoleConfiguration : IEntityTypeConfiguration<Role>
{
builder.ToTable(nameof(Role));
builder.Property(role => role.Id).ValueGeneratedNever();
builder.Property(role => role.Name)
.HasMaxLength(50);
......
namespace Persistence.Identity.PasswordsHashing;
public interface IPasswordHasher
{
public string Hash(string password);
public bool Verify(string password, string passwordHash);
}
using System.Security.Cryptography;
namespace Persistence.Identity.PasswordsHashing;
public class PasswordHasher : IPasswordHasher
{
#region Hashing
// Salting is adding a random number to the hash
// to ensure that users with same passwords have
// different hash values.
// Best pactice is to use 128 bits = 16 bytes
private const int SaltSize = 16;
// Output size
// Best practice is to use 256 bits
private const int HashSize = 32;
private const int Iterations = 100000;
private readonly HashAlgorithmName Algorithm = HashAlgorithmName.SHA512;
public string Hash(string password)
{
byte[] salt = RandomNumberGenerator.GetBytes(SaltSize);
byte[] hash = Rfc2898DeriveBytes.Pbkdf2(password, salt, Iterations, Algorithm, HashSize);
// Pdkdf ~ Password Based Key Derivation Function
return $"{Convert.ToHexString(hash)}-{Convert.ToHexString(salt)}";
}
#endregion
#region Verification
public bool Verify(string password, string passwordHash)
{
string[] parts = passwordHash.Split('-');
byte[] hash = Convert.FromHexString(parts[0]);
byte[] salt = Convert.FromHexString(parts[1]);
byte[] inputHash = Rfc2898DeriveBytes.Pbkdf2(password, salt, Iterations, Algorithm, HashSize);
return CryptographicOperations.FixedTimeEquals(hash, inputHash);
}
#endregion
}
......@@ -12,7 +12,7 @@ using Persistence.Context;
namespace Persistence.Migrations
{
[DbContext(typeof(ClinicsDbContext))]
[Migration("20240820202107_Add_Identity")]
[Migration("20240820212742_Add_Identity")]
partial class Add_Identity
{
/// <inheritdoc />
......@@ -28,11 +28,8 @@ namespace Persistence.Migrations
modelBuilder.Entity("Domain.Entities.Identity.UserRoles.Role", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50)
......
......@@ -14,8 +14,7 @@ namespace Persistence.Migrations
name: "Role",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Id = table.Column<int>(type: "int", nullable: false),
Name = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false)
},
constraints: table =>
......
......@@ -25,11 +25,8 @@ namespace Persistence.Migrations
modelBuilder.Entity("Domain.Entities.Identity.UserRoles.Role", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50)
......
using Domain.Entities.Identity.UserRoles;
using Microsoft.EntityFrameworkCore;
using Persistence.Context;
namespace Persistence.SeedDatabase;
public class UserRoles : ISeed<Role>
{
#region CTOR DI
private readonly ClinicsDbContext _context;
public UserRoles(ClinicsDbContext context)
{
_context = context;
}
#endregion
public async Task Seed()
{
DbSet<Role> roles = _context.Set<Role>();
var current = await roles.ToListAsync();
// TODO: perform deep check on all seed operations
if (current.Count != Roles.Count)
{
roles.RemoveRange(current);
roles.Add(Roles.Admin);
roles.Add(Roles.Doctor);
roles.Add(Roles.Receptionist);
await _context.SaveChangesAsync();
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment