From db2df2460f1dc53f7e76846b493043441fc9783b Mon Sep 17 00:00:00 2001 From: Jeas0001 Date: Thu, 20 Mar 2025 14:16:10 +0100 Subject: [PATCH] Middle logic maybe done --- backend/Api/BusinessLogic/DeviceLogic.cs | 63 +++++++++ backend/Api/BusinessLogic/UserLogic.cs | 138 ++++++++++++++++++++ backend/Api/Controllers/DeviceController.cs | 19 ++- backend/Api/Controllers/UserController.cs | 48 ++----- backend/Api/DBAccess/DBAccess.cs | 115 +++++++++------- backend/Api/Startup.cs | 4 +- 6 files changed, 288 insertions(+), 99 deletions(-) create mode 100644 backend/Api/BusinessLogic/DeviceLogic.cs create mode 100644 backend/Api/BusinessLogic/UserLogic.cs diff --git a/backend/Api/BusinessLogic/DeviceLogic.cs b/backend/Api/BusinessLogic/DeviceLogic.cs new file mode 100644 index 0000000..506316c --- /dev/null +++ b/backend/Api/BusinessLogic/DeviceLogic.cs @@ -0,0 +1,63 @@ +using Api.DBAccess; +using Api.Models; +using Microsoft.AspNetCore.Mvc; +using static System.Runtime.InteropServices.JavaScript.JSType; + +namespace Api.BusinessLogic +{ + public class DeviceLogic + { + private readonly DbAccess _dbAccess; + private readonly IConfiguration _configuration; + + public DeviceLogic(IConfiguration configuration, DbAccess dbAccess) + { + _dbAccess = dbAccess; + _configuration = configuration; + } + + public async Task GetDevices(int userId) + { + var profile = await _dbAccess.ReadUser(userId); + + if (profile == null) { return new ConflictObjectResult(new { message = "Could not find user" }); } + + var devices = await _dbAccess.ReadDevices(userId); + + if (devices.Count == 0) { return new ConflictObjectResult(new { message = "Could not find any devices connected to the user" }); } + + return new OkObjectResult(devices); + } + + public async Task AddDevice(Device device, int userId) + { + var profile = await _dbAccess.ReadUser(userId); + + if (profile == null) { return new ConflictObjectResult(new { message = "Could not find user" }); } + + return await _dbAccess.CreateDevice(device, userId); + } + + public async Task GetLogs(int deviceId) + { + var device = await _dbAccess.ReadDevice(deviceId); + + if (device == null) { return new ConflictObjectResult(new { message = "Could not find device" }); } + + var logs = await _dbAccess.ReadLogs(deviceId); + + if (logs.Count == 0) { return new ConflictObjectResult(new { message = "Could not find any logs connected to the device" }); } + + return new OkObjectResult(logs); + } + + public async Task EditDevice(Device device, int deviceId) + { + var device1 = _dbAccess.ReadDevice(deviceId); + + if (device1 == null) { return new ConflictObjectResult(new { message = "Could not find device" }); } + + return await _dbAccess.UpdateDevice(device, deviceId); + } + } +} diff --git a/backend/Api/BusinessLogic/UserLogic.cs b/backend/Api/BusinessLogic/UserLogic.cs new file mode 100644 index 0000000..43d21f6 --- /dev/null +++ b/backend/Api/BusinessLogic/UserLogic.cs @@ -0,0 +1,138 @@ +using Api.DBAccess; +using Api.Models; +using Microsoft.AspNetCore.Http.HttpResults; +using Microsoft.AspNetCore.Mvc; +using Microsoft.IdentityModel.Tokens; +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; + +namespace Api.BusinessLogic +{ + public class UserLogic + { + private readonly DbAccess _dbAccess; + private readonly IConfiguration _configuration; + + public UserLogic(IConfiguration configuration, DbAccess dbAccess) + { + _dbAccess = dbAccess; + _configuration = configuration; + } + + public async Task RegisterUser(User user) + { + if (!new Regex(@".+@.+\..+").IsMatch(user.Email)) + { + return new ConflictObjectResult(new { message = "Invalid email address" }); + } + + if (!PasswordSecurity(user.Password)) + { + return new ConflictObjectResult(new { message = "Password is not up to the security standard" }); + } + + if (user.Devices == null) + { + user.Devices = new List(); + } + + string salt = Guid.NewGuid().ToString(); + string hashedPassword = ComputeHash(user.Password, SHA256.Create(), salt); + + user.Salt = salt; + user.Password = hashedPassword; + + return await _dbAccess.CreateUser(user); + } + + public async Task Login(Login login) + { + User user = await _dbAccess.Login(login); + + if (user == null || user.Id == 0) { return new ConflictObjectResult(new { message = "Could not find user" }); } + + string hashedPassword = ComputeHash(login.Password, SHA256.Create(), user.Salt); + + if (user.Password == hashedPassword) + { + var token = GenerateJwtToken(user); + return new OkObjectResult(new { token, user.UserName, user.Id }); + } + + return new ConflictObjectResult(new { message = "Invalid password" }); + } + + public async Task EditProfile(User user, int userId) + { + if (!new Regex(@".+@.+\..+").IsMatch(user.Email)) + { + return new ConflictObjectResult(new { message = "Invalid email address" }); + } + + if (!PasswordSecurity(user.Password)) + { + return new ConflictObjectResult(new { message = "Password is not up to the security standard" }); + } + + var profile = await _dbAccess.ReadUser(userId); + + string hashedPassword = ComputeHash(user.Password, SHA256.Create(), profile.Salt); + user.Password = hashedPassword; + + return await _dbAccess.UpdateUser(user, userId); + } + + public async Task DeleteUser(int userId) + { + return await _dbAccess.DeleteUser(userId); + } + + private static string ComputeHash(string input, HashAlgorithm algorithm, string salt) + { + Byte[] inputBytes = Encoding.UTF8.GetBytes(input); + Byte[] saltBytes = Encoding.UTF8.GetBytes(salt); + + // Combine salt and input bytes + Byte[] saltedInput = new Byte[saltBytes.Length + inputBytes.Length]; + saltBytes.CopyTo(saltedInput, 0); + inputBytes.CopyTo(saltedInput, saltBytes.Length); + + Byte[] hashedBytes = algorithm.ComputeHash(saltedInput); + + return BitConverter.ToString(hashedBytes); + } + + public bool PasswordSecurity(string password) + { + var hasMinimum8Chars = new Regex(@".{8,}"); + + return hasMinimum8Chars.IsMatch(password); + } + + private string GenerateJwtToken(User user) + { + var claims = new[] + { + new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString()), + new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), + new Claim(ClaimTypes.Name, user.UserName) + }; + + var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes + (_configuration["JwtSettings:Key"])); + var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); + + var token = new JwtSecurityToken( + _configuration["JwtSettings:Issuer"], + _configuration["JwtSettings:Audience"], + claims, + expires: DateTime.Now.AddMinutes(30), + signingCredentials: creds); + + return new JwtSecurityTokenHandler().WriteToken(token); + } + } +} diff --git a/backend/Api/Controllers/DeviceController.cs b/backend/Api/Controllers/DeviceController.cs index c21a8a8..b1109e8 100644 --- a/backend/Api/Controllers/DeviceController.cs +++ b/backend/Api/Controllers/DeviceController.cs @@ -2,6 +2,7 @@ using Api.Models; using Api.DBAccess; using Microsoft.AspNetCore.Authorization; +using Api.BusinessLogic; namespace Api.Controllers { @@ -10,10 +11,12 @@ namespace Api.Controllers public class DeviceController : Controller { private readonly DbAccess _dbAccess; + private readonly DeviceLogic _deviceLogic; - public DeviceController(DbAccess dbAccess) + public DeviceController(DbAccess dbAccess, DeviceLogic deviceLogic) { _dbAccess = dbAccess; + _deviceLogic = deviceLogic; } [Authorize] @@ -22,34 +25,28 @@ namespace Api.Controllers { List devices = await _dbAccess.ReadDevices(userId); if (devices.Count == 0) { return BadRequest(new { error = "There is no devices that belong to this userID" }); } - return Ok(devices); + return await _deviceLogic.GetDevices(userId); } [Authorize] [HttpPost("adddevice/{userId}")] public async Task AddDevice([FromBody] Device device, int userId) { - bool success = await _dbAccess.CreateDevice(device, userId); - if (!success) { return BadRequest(new { error = "This device already exist" }); } - return Ok(); + return await _deviceLogic.AddDevice(device, userId); } [Authorize] [HttpGet("logs/{deviceId}")] public async Task GetLogs(int deviceId) { - List logs = await _dbAccess.ReadLogs(deviceId); - if (logs.Count == 0) { return BadRequest(new { error = "There is no logs that belong to this deviceId" }); } - return Ok(logs); + return await _deviceLogic.GetLogs(deviceId); } [Authorize] [HttpPut("Edit/{deviceId}")] public async Task EditDevice([FromBody] Device device, int deviceId) { - bool success = await _dbAccess.UpdateDevice(device, deviceId); - if (!success) { return BadRequest(new { error = "Device can't be edited" }); } - return Ok(); + return await _deviceLogic.EditDevice(device, deviceId); } } } diff --git a/backend/Api/Controllers/UserController.cs b/backend/Api/Controllers/UserController.cs index d28d521..186e1d4 100644 --- a/backend/Api/Controllers/UserController.cs +++ b/backend/Api/Controllers/UserController.cs @@ -6,6 +6,7 @@ using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; using Microsoft.AspNetCore.Authorization; +using Api.BusinessLogic; namespace Api.Controllers { @@ -13,71 +14,38 @@ namespace Api.Controllers [Route("api/[controller]")] public class UserController : Controller { - private readonly DbAccess _dbAccess; - private readonly IConfiguration _configuration; + private readonly UserLogic _userLogic; - public UserController(IConfiguration configuration, DbAccess dbAccess) + public UserController(UserLogic userLogic) { - _dbAccess = dbAccess; - _configuration = configuration; + _userLogic = userLogic; } [HttpPost("Login")] public async Task Login([FromBody] Login login) { - User user = await _dbAccess.Login(login); - if (user == null || user.Id == 0) { return Unauthorized(new { error = "Invalid username or password" }); } - var token = GenerateJwtToken(user); - return Ok(new { token, user.UserName, user.Id }); + return await _userLogic.Login(login); } [HttpPost("Create")] public async Task CreateUser([FromBody] User user) { - bool success = await _dbAccess.CreateUser(user); - if (!success) { return BadRequest(new { error = "User can't be created" }); } - return Ok(); + return await _userLogic.RegisterUser(user); } [Authorize] [HttpPut("Edit/{userId}")] public async Task EditUser([FromBody] User user, int userId) { - bool success = await _dbAccess.UpdateUser(user, userId); - if (!success) { return BadRequest(new { error = "User can't be edited" }); } - return Ok(); + return await _userLogic.EditProfile(user, userId); } [Authorize] [HttpDelete("Delete/{userId}")] public async Task DeleteUser(int userId) { - bool success = await _dbAccess.DeleteUser(userId); - if (!success) { return BadRequest(new { error = "User can't be deleted" }); } - return Ok(); + return await _userLogic.DeleteUser(userId); } - private string GenerateJwtToken(User user) - { - var claims = new[] - { - new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString()), - new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), - new Claim(ClaimTypes.Name, user.UserName) - }; - - var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes - (_configuration["JwtSettings:Key"])); - var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); - - var token = new JwtSecurityToken( - _configuration["JwtSettings:Issuer"], - _configuration["JwtSettings:Audience"], - claims, - expires: DateTime.Now.AddMinutes(30), - signingCredentials: creds); - - return new JwtSecurityTokenHandler().WriteToken(token); - } } } diff --git a/backend/Api/DBAccess/DBAccess.cs b/backend/Api/DBAccess/DBAccess.cs index 9a69c6a..19a46fd 100644 --- a/backend/Api/DBAccess/DBAccess.cs +++ b/backend/Api/DBAccess/DBAccess.cs @@ -3,6 +3,8 @@ using Api.Models; using System.Text; using System.Runtime.Intrinsics.Arm; using System.Security.Cryptography; +using Microsoft.AspNetCore.Mvc; +using static System.Runtime.InteropServices.JavaScript.JSType; namespace Api.DBAccess @@ -16,29 +18,26 @@ namespace Api.DBAccess _context = context; } - public async Task CreateUser(User user) + public async Task CreateUser(User user) { var users = await _context.Users.ToListAsync(); foreach (var item in users) { - if (item.UserName == user.UserName || item.Email == user.Email) + if (item.UserName == user.UserName) { - return false; + return new ConflictObjectResult(new { message = "Username is already in use." }); + } + + if (item.Email == user.Email) + { + return new ConflictObjectResult(new { message = "Email is being used already" }); } } - if (user.Devices == null) - { - user.Devices = new List(); - } - string salt = Guid.NewGuid().ToString(); - string hashedPassword = ComputeHash(user.Password, SHA256.Create(), salt); - - user.Salt = salt; - user.Password = hashedPassword; _context.Users.Add(user); - return await _context.SaveChangesAsync() == 1; + + return new OkObjectResult(await _context.SaveChangesAsync()); } public async Task Login(Login login) @@ -53,22 +52,34 @@ namespace Api.DBAccess user = await _context.Users.FirstOrDefaultAsync(u => u.Email == login.EmailOrUsrn); } - if (user == null) { return new User(); } - - string hashedPassword = ComputeHash(user.Password, SHA256.Create(), user.Salt); - - if (hashedPassword == user.Password) - { - return user; - } - return new User(); + if (user == null || user.Id == 0) { return new User(); } + return user; } - public async Task UpdateUser(User user, int userId) + public async Task ReadUser(int userId) + { + return await _context.Users.FirstOrDefaultAsync(u => u.Id == userId); + } + + public async Task UpdateUser(User user, int userId) { var profile = await _context.Users.FirstOrDefaultAsync(u => u.Id == userId); + var users = await _context.Users.ToListAsync(); - if (profile == null) { return false; } + if (profile == null) { return new ConflictObjectResult(new { message = "User does not exist" }); } + + foreach (var item in users) + { + if (item.UserName == user.UserName) + { + return new ConflictObjectResult(new { message = "Username is already in use." }); + } + + if (item.Email == user.Email) + { + return new ConflictObjectResult(new { message = "Email is being used already" }); + } + } profile.UserName = user.UserName; @@ -76,10 +87,14 @@ namespace Api.DBAccess profile.Password = user.Password; - return await _context.SaveChangesAsync() == 1; + bool saved = await _context.SaveChangesAsync() == 1; + + if (saved) { return new OkObjectResult(profile); } + + return new ConflictObjectResult(new { message = "Could not save to database" }); } - public async Task DeleteUser(int userId) + public async Task DeleteUser(int userId) { var user = await _context.Users.Include(u => u.Devices).FirstOrDefaultAsync(u => u.Id == userId); if (user != null) @@ -93,9 +108,13 @@ namespace Api.DBAccess } } _context.Users.Remove(user); - return await _context.SaveChangesAsync() == 1; + bool saved = await _context.SaveChangesAsync() == 1; + + if (saved) { return new OkObjectResult(saved); } + + return new ConflictObjectResult(new { message = "Could not save to database" }); } - return false; + return new ConflictObjectResult(new { message = "Invalid user" }); } public async Task> ReadDevices(int userId) @@ -109,23 +128,35 @@ namespace Api.DBAccess return devices; } - public async Task CreateDevice(Device device, int userId) + public async Task CreateDevice(Device device, int userId) { var user = await _context.Users.Include(u => u.Devices).FirstOrDefaultAsync(u => u.Id == userId); - if (user == null || user.Devices == null) { return false; } + if (user == null || user.Devices == null) { return new ConflictObjectResult(new { message = "User did not have a device list" }); } if (device.Logs == null) { device.Logs = new List(); } user.Devices.Add(device); - return await _context.SaveChangesAsync() == 1; + bool saved = await _context.SaveChangesAsync() == 1; + + if (saved) { return new OkObjectResult(saved); } + + return new ConflictObjectResult(new { message = "Could not save to database" }); } - public async Task UpdateDevice(Device device, int deviceId) + public async Task ReadDevice(int deviceId) + { + return await _context.Devices.FirstOrDefaultAsync(d => d.Id == deviceId); + } + + + public async Task UpdateDevice(Device device, int deviceId) { var device1 = await _context.Devices.FirstOrDefaultAsync(u => u.Id == deviceId); + if (device1 == null) { return new ConflictObjectResult(new { message = "Device does not exist" }); } + device1.TempLow = device.TempLow; device1.TempHigh = device.TempHigh; @@ -134,7 +165,11 @@ namespace Api.DBAccess device1.Name = device.Name; - return await _context.SaveChangesAsync() == 1; + bool saved = await _context.SaveChangesAsync() == 1; + + if (saved) { return new OkObjectResult(device1); } + + return new ConflictObjectResult(new { message = "Could not save to database" }); } public async Task> ReadLogs(int deviceId) @@ -153,19 +188,5 @@ namespace Api.DBAccess return _context.Database.CanConnect(); } - private static string ComputeHash(string input, HashAlgorithm algorithm, string salt) - { - Byte[] inputBytes = Encoding.UTF8.GetBytes(input); - Byte[] saltBytes = Encoding.UTF8.GetBytes(salt); - - // Combine salt and input bytes - Byte[] saltedInput = new Byte[saltBytes.Length + inputBytes.Length]; - saltBytes.CopyTo(saltedInput, 0); - inputBytes.CopyTo(saltedInput, saltBytes.Length); - - Byte[] hashedBytes = algorithm.ComputeHash(saltedInput); - - return BitConverter.ToString(hashedBytes); - } } } diff --git a/backend/Api/Startup.cs b/backend/Api/Startup.cs index 4edb391..55b68fe 100644 --- a/backend/Api/Startup.cs +++ b/backend/Api/Startup.cs @@ -1,4 +1,5 @@ -using Api.DBAccess; +using Api.BusinessLogic; +using Api.DBAccess; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; @@ -25,6 +26,7 @@ namespace Api options.UseSqlite(_configuration.GetConnectionString("Database"))); services.AddScoped(); + services.AddScoped(); services.AddControllers();