236 lines
8.2 KiB
C#
236 lines
8.2 KiB
C#
using API.DBAccess;
|
|
using API.Models.UserModels;
|
|
using API.Models.RecipeModels;
|
|
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;
|
|
using API.Models.ShoppingListModels;
|
|
|
|
namespace API.BusinessLogic
|
|
{
|
|
public class UserLogic
|
|
{
|
|
private readonly UserDBAccess _dbAccess;
|
|
private readonly IConfiguration _configuration;
|
|
|
|
public UserLogic(IConfiguration configuration, UserDBAccess dbAccess)
|
|
{
|
|
_dbAccess = dbAccess;
|
|
_configuration = configuration;
|
|
}
|
|
|
|
public async Task<IActionResult> GetUser(int userId)
|
|
{
|
|
User user = await _dbAccess.ReadUser(userId);
|
|
|
|
if (user == null || user.Id == 0) { return new ConflictObjectResult(new { message = "Could not find user" }); }
|
|
return new OkObjectResult(new { user.Id, user.UserName, user.Email });
|
|
}
|
|
|
|
public async Task<IActionResult> RegisterUser(CreateUserDTO userDTO)
|
|
{
|
|
if (!EmailCheck(userDTO.Email))
|
|
{
|
|
return new ConflictObjectResult(new { message = "Invalid email address" });
|
|
}
|
|
|
|
if (!PasswordSecurity(userDTO.Password))
|
|
{
|
|
return new ConflictObjectResult(new { message = "Password is not up to the security standard" });
|
|
}
|
|
|
|
if (await _dbAccess.UserNameInUse(userDTO.UserName))
|
|
{
|
|
return new ConflictObjectResult(new { message = "Username is already in use." });
|
|
}
|
|
|
|
if (await _dbAccess.EmailInUse(userDTO.Email))
|
|
{
|
|
return new ConflictObjectResult(new { message = "Email is already in use." });
|
|
}
|
|
|
|
string salt = Guid.NewGuid().ToString();
|
|
string hashedPassword = ComputeHash(userDTO.Password, SHA256.Create(), salt);
|
|
|
|
|
|
|
|
User user = new User
|
|
{
|
|
UserName = userDTO.UserName,
|
|
Email = userDTO.Email,
|
|
Password = hashedPassword,
|
|
Salt = salt,
|
|
Recipes = new List<Recipe>(),
|
|
ShoppingList = new List<ShoppingList>(),
|
|
};
|
|
|
|
return await _dbAccess.CreateUser(user);
|
|
}
|
|
|
|
public async Task<IActionResult> Login(LoginDTO loginDTO)
|
|
{
|
|
var user = await _dbAccess.ReadUserForLogin(loginDTO.EmailUsr);
|
|
|
|
if (user == null || user.Id == 0) { return new ConflictObjectResult(new { message = "Could not find user" }); }
|
|
|
|
string hashedPassword = ComputeHash(loginDTO.Password, SHA256.Create(), user.Salt);
|
|
|
|
if (user.Password == hashedPassword)
|
|
{
|
|
var token = GenerateJwtToken(user);
|
|
|
|
user = await UpdateRefreshToken(user);
|
|
|
|
return new OkObjectResult(new { token, user.UserName, user.Id, refreshToken = user.RefreshToken });
|
|
}
|
|
|
|
return new ConflictObjectResult(new { message = "Invalid password" });
|
|
}
|
|
|
|
public async Task<IActionResult> EditProfile(UpdateUserDTO userDTO, int userId)
|
|
{
|
|
var profile = await _dbAccess.ReadUser(userId);
|
|
|
|
if (profile == null) { return new ConflictObjectResult(new { message = "User does not exist" }); }
|
|
|
|
if (!EmailCheck(userDTO.Email))
|
|
{
|
|
return new ConflictObjectResult(new { message = "Invalid email address" });
|
|
}
|
|
|
|
if (userDTO.Email == "" || userDTO.Email == null)
|
|
{
|
|
return new ConflictObjectResult(new { message = "Please enter an email" });
|
|
}
|
|
|
|
if (userDTO.UserName == "" || userDTO.UserName == null)
|
|
{
|
|
return new ConflictObjectResult(new { message = "Please enter an userName" });
|
|
}
|
|
|
|
|
|
if (await _dbAccess.UserNameInUse(userDTO.UserName))
|
|
{
|
|
return new ConflictObjectResult(new { message = "Username is already in use." });
|
|
}
|
|
|
|
if (await _dbAccess.EmailInUse(userDTO.Email))
|
|
{
|
|
return new ConflictObjectResult(new { message = "Email is already in use." });
|
|
}
|
|
|
|
|
|
|
|
profile.Email = userDTO.Email;
|
|
profile.UserName = userDTO.UserName;
|
|
|
|
return await _dbAccess.UpdateUser(profile);
|
|
}
|
|
|
|
public async Task<IActionResult> ChangePassword(ChangePasswordDTO passwordDTO, int userId)
|
|
{
|
|
var user = await _dbAccess.ReadUser(userId);
|
|
|
|
if (user == null) { return new ConflictObjectResult(new { message = "User does not exist" }); }
|
|
|
|
string hashedPassword = ComputeHash(passwordDTO.OldPassword, SHA256.Create(), user.Salt);
|
|
|
|
if (user.Password != hashedPassword)
|
|
{
|
|
return new ConflictObjectResult(new { message = "Old password is incorrect" });
|
|
|
|
}
|
|
|
|
if (!PasswordSecurity(passwordDTO.NewPassword))
|
|
{
|
|
return new ConflictObjectResult(new { message = "New password is not up to the security standard" });
|
|
}
|
|
|
|
string hashedNewPassword = ComputeHash(passwordDTO.NewPassword, SHA256.Create(), user.Salt);
|
|
user.Password = hashedNewPassword;
|
|
|
|
return await _dbAccess.UpdatePassword(user);
|
|
}
|
|
|
|
public async Task<IActionResult> DeleteUser(int userId)
|
|
{
|
|
var user = await _dbAccess.ReadUserForDelete(userId);
|
|
|
|
if (user != null) { return await _dbAccess.DeleteUser(user); }
|
|
|
|
return new ConflictObjectResult(new { message = "Invalid user" });
|
|
}
|
|
|
|
public async Task<IActionResult> RefreshToken(string refreshToken)
|
|
{
|
|
User user = await _dbAccess.ReadUserByRefreshToken(refreshToken);
|
|
if (user == null) { return new ConflictObjectResult(new { message = "Could not match refreshtoken" }); }
|
|
user = await UpdateRefreshToken(user);
|
|
string jwtToken = GenerateJwtToken(user);
|
|
return new OkObjectResult(new { token = jwtToken, refreshToken = user.RefreshToken });
|
|
}
|
|
|
|
private bool PasswordSecurity(string password)
|
|
{
|
|
var hasMinimum8Chars = new Regex(@".{8,}");
|
|
|
|
return hasMinimum8Chars.IsMatch(password);
|
|
}
|
|
|
|
private bool EmailCheck(string email)
|
|
{
|
|
return new Regex(@".+@.+\..+").IsMatch(email);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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.AddHours(1),
|
|
signingCredentials: creds);
|
|
|
|
return new JwtSecurityTokenHandler().WriteToken(token);
|
|
}
|
|
|
|
private async Task<User> UpdateRefreshToken(User user)
|
|
{
|
|
user.RefreshToken = Guid.NewGuid().ToString();
|
|
user.RefreshTokenExpireAt = DateTime.Now.AddDays(30);
|
|
await _dbAccess.UpdateUser(user);
|
|
return user;
|
|
}
|
|
}
|
|
}
|