Create models and Clean Arch boilerplate
This commit is contained in:
parent
3926db5446
commit
872dc1e263
21 changed files with 576 additions and 27 deletions
|
@ -6,4 +6,8 @@
|
|||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<InternalsVisibleTo Include="CSR.Infrastructure" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
33
CSR.Domain/Entities/Role.cs
Normal file
33
CSR.Domain/Entities/Role.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
namespace CSR.Domain.Entities;
|
||||
|
||||
public record Role
|
||||
{
|
||||
public int Id { get; private set; }
|
||||
public string Name { get; private set; }
|
||||
|
||||
private Role(int id, string name)
|
||||
{
|
||||
Id = id;
|
||||
Name = name;
|
||||
}
|
||||
|
||||
internal static Role LoadExisting(int id, string name)
|
||||
{
|
||||
return new Role(id, name);
|
||||
}
|
||||
|
||||
public static Role Admin
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Role(1, "Admin");
|
||||
}
|
||||
}
|
||||
public static Role User
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Role(2, "User");
|
||||
}
|
||||
}
|
||||
}
|
173
CSR.Domain/Entities/User.cs
Normal file
173
CSR.Domain/Entities/User.cs
Normal file
|
@ -0,0 +1,173 @@
|
|||
namespace CSR.Domain.Entities;
|
||||
|
||||
using System.Net.Mail;
|
||||
using CSR.Domain.Interfaces;
|
||||
|
||||
public class User
|
||||
{
|
||||
public int Id { get; private set; }
|
||||
public string Username { get; private set; }
|
||||
public string Email { get; private set; }
|
||||
public string PasswordHash { get; private set; }
|
||||
|
||||
public int RoleId { get; private set; }
|
||||
public Role Role { get; private set; }
|
||||
|
||||
private User(int id, string username, string email, string passwordHash, int roleId, Role role)
|
||||
{
|
||||
Id = id;
|
||||
Username = username;
|
||||
Email = email;
|
||||
PasswordHash = passwordHash;
|
||||
RoleId = roleId;
|
||||
Role = role;
|
||||
}
|
||||
|
||||
// This role is used when a new user is created
|
||||
public static readonly Role DefaultRole = Role.User;
|
||||
|
||||
|
||||
internal static User LoadExisting(int id, string username, string email, string passwordHash, int roleId, Role role)
|
||||
{
|
||||
return new User(id, username, email, passwordHash, roleId, role);
|
||||
}
|
||||
|
||||
|
||||
public (bool Success, IEnumerable<string>? Errors) UpdatePassword(string password, IPasswordHasher passwordHasher, User requestingUser)
|
||||
{
|
||||
if (requestingUser.Id != Id || requestingUser.Role.Name != "Admin")
|
||||
{
|
||||
throw new UnauthorizedAccessException("Only admins or the same user can update passwords.");
|
||||
}
|
||||
|
||||
var validityCheck = IsValidPassword(password);
|
||||
|
||||
if (validityCheck.IsValid)
|
||||
{
|
||||
PasswordHash = passwordHasher.HashPassword(password);
|
||||
}
|
||||
|
||||
return validityCheck;
|
||||
}
|
||||
|
||||
|
||||
public bool VerifyPasswordAgainstHash(string providedPassword, IPasswordHasher passwordHasher, User requestingUser)
|
||||
{
|
||||
if (requestingUser.Id != Id || requestingUser.Role.Name != "Admin")
|
||||
{
|
||||
throw new UnauthorizedAccessException("Only admins or the same user can verify passwords.");
|
||||
}
|
||||
|
||||
return passwordHasher.VerifyHashedPassword(PasswordHash, providedPassword);
|
||||
}
|
||||
|
||||
|
||||
public static (bool IsValid, IEnumerable<string>? Errors) IsValidPassword(string password)
|
||||
{
|
||||
var Errors = new List<string>();
|
||||
if (password.Length < 8 || password.Length > 32)
|
||||
{
|
||||
Errors.Add("Password must be between 8 and 32 characters long.");
|
||||
}
|
||||
if (!password.Any(char.IsUpper))
|
||||
{
|
||||
Errors.Add("Password must contain at least one uppercase letter.");
|
||||
}
|
||||
if (!password.Any(char.IsLower))
|
||||
{
|
||||
Errors.Add("Password must contain at least one lowercase letter.");
|
||||
}
|
||||
if (!password.Any(char.IsDigit))
|
||||
{
|
||||
Errors.Add("Password must contain at least one digit.");
|
||||
}
|
||||
if (Errors.Count > 0)
|
||||
{
|
||||
return (false, Errors);
|
||||
}
|
||||
return (true, null);
|
||||
}
|
||||
|
||||
|
||||
public (bool Success, IEnumerable<string>? Errors) UpdateEmail(string email, User requestingUser)
|
||||
{
|
||||
if (requestingUser.Id != Id || requestingUser.Role.Name != "Admin")
|
||||
{
|
||||
throw new UnauthorizedAccessException("Only admins or the same user can update email.");
|
||||
}
|
||||
|
||||
if (IsValidEmail(email))
|
||||
{
|
||||
Email = email;
|
||||
return (true, null);
|
||||
}
|
||||
return (false, new List<string> { "Invalid email format." });
|
||||
}
|
||||
|
||||
|
||||
public static bool IsValidEmail(string email)
|
||||
{
|
||||
try
|
||||
{
|
||||
var mailAddress = new MailAddress(email);
|
||||
return true;
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public (bool Success, IEnumerable<string>? Errors) UpdateUsername(string username, User requestingUser)
|
||||
{
|
||||
if (requestingUser.Id != Id || requestingUser.Role.Name != "Admin")
|
||||
{
|
||||
throw new UnauthorizedAccessException("Only admins can update username.");
|
||||
}
|
||||
|
||||
var validityCheck = IsValidUsername(username);
|
||||
if (validityCheck.IsValid)
|
||||
{
|
||||
Username = username;
|
||||
}
|
||||
|
||||
return validityCheck;
|
||||
}
|
||||
|
||||
|
||||
public static (bool IsValid, IEnumerable<string>? Errors) IsValidUsername(string username)
|
||||
{
|
||||
var Errors = new List<string>();
|
||||
if (!username.All(char.IsLetterOrDigit))
|
||||
{
|
||||
Errors.Add("Username must be alphanumeric.");
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(username) || username.Length < 3)
|
||||
{
|
||||
Errors.Add("Username must be at least 3 characters long.");
|
||||
}
|
||||
if (username.Length > 32)
|
||||
{
|
||||
Errors.Add("Username must be less than 32 characters long.");
|
||||
}
|
||||
|
||||
if (Errors.Count > 0)
|
||||
{
|
||||
return (false, Errors);
|
||||
}
|
||||
|
||||
return (true, null);
|
||||
}
|
||||
|
||||
|
||||
public void UpdateRole(Role role, User requestingUser)
|
||||
{
|
||||
if (requestingUser.Role.Name != "Admin")
|
||||
{
|
||||
throw new UnauthorizedAccessException("Only admins can assign roles.");
|
||||
}
|
||||
Role = role;
|
||||
RoleId = role.Id;
|
||||
}
|
||||
}
|
7
CSR.Domain/Interfaces/IPasswordHasher.cs
Normal file
7
CSR.Domain/Interfaces/IPasswordHasher.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace CSR.Domain.Interfaces;
|
||||
|
||||
public interface IPasswordHasher
|
||||
{
|
||||
string HashPassword(string password);
|
||||
bool VerifyHashedPassword(string hashedPassword, string providedPassword);
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
namespace CSR.Domain;
|
||||
|
||||
public class Role
|
||||
{
|
||||
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
namespace CSR.Domain;
|
||||
|
||||
public class User
|
||||
{
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue