using API.DTO.Base; using API.DTO.Base.Update; using API.DTO.Login; using API.Hashing.Interfaces; using API.Services.Interfaces; using DAL.Models; namespace API.Services { public class UserManager : IUserManager { private readonly IHashingFactory _hashingFactory; private readonly ILogger _logger; private readonly HashingType _preferredHashingType; private readonly UserService _userService; public UserManager(UserService userService, IHashingFactory hashingFactory, ILogger logger, HashingType preferredHashingType) { _userService = userService; _hashingFactory = hashingFactory; _logger = logger; _preferredHashingType = preferredHashingType; } public UserDTO? authenticateUser(UserLoginDTO loginDTO) { User? user = _userService.getNoAuthentication(x => x.phoneNumber.Equals(loginDTO.phoneNumber)).FirstOrDefault(); if (user == null) return null; IHashingAlgorithm? hashingAlgorithm = _hashingFactory.getAlgorithm(user.hashingType); if (hashingAlgorithm == null) { _logger.Log(LogLevel.Warning, "User id '{id}' has a hashing type '{hashingType}' that isn't recognized by factory '{factory}'. Not logging in.", user.id, user.hashingType, nameof(_hashingFactory)); return null; } string hashedPassword = hashingAlgorithm.hash(loginDTO.password, user.salt); if (!hashedPassword.Equals(user.password)) { _logger.Log(LogLevel.Information, "Failed login attempt for user id '{id}.", user.id); return null; } if (user.hashingType != _preferredHashingType) { // todo The user is logged in at this point. Their hashing type needs to be updated, we need to rehash & salt the password and save it now. } UserDTO dto = new UserDTO(); dto.adaptFromModel(user); return dto; } public UserDTO? registerUser(UserRegisterDTO registerDTO, User? user = null, ulong? permissionId = null) { if (_userService.getNoAuthentication(x => x.phoneNumber.Equals(registerDTO.phoneNumber) || x.firstName.Equals(registerDTO.firstName) && x.lastName.Equals(registerDTO.lastName)) .Any()) { return null; } IHashingAlgorithm? hashingAlgorithm = _hashingFactory.getAlgorithm(_preferredHashingType); if (hashingAlgorithm == null) { _logger.Log(LogLevel.Error, "Preferred hashing type '{hashingType}' that isn't recognized by factory '{factory}'.", _preferredHashingType, nameof(_hashingFactory)); return null; } byte[] salt; string hashedPassword = hashingAlgorithm.hash(registerDTO.password, out salt); User? createdUser = _userService.add(registerDTO, hashedPassword, salt, user, permissionId); if (createdUser == null) return null; UserDTO dto = new UserDTO(); dto.adaptFromModel(createdUser); return dto; } public UserDTO? changePassword(UserPasswordUpdateDTO userPasswordUpdateDTO, User changingUser) { User? destUser = _userService.getNoAuthentication(x => x.phoneNumber.Equals(userPasswordUpdateDTO.phoneNumber)).FirstOrDefault(); if (destUser == null) return null; IHashingAlgorithm? hashingAlgorithm = _hashingFactory.getAlgorithm(_preferredHashingType); if (hashingAlgorithm == null){ _logger.Log(LogLevel.Error, "Preferred hashing type '{hashingType}' that isn't recognized by factory '{factory}'.", _preferredHashingType, nameof(_hashingFactory)); return null; } byte[] newSalt; string hashedNewPassword = hashingAlgorithm.hash(userPasswordUpdateDTO.newPassword, out newSalt); bool oldPasswordMatchNew = false; if (userPasswordUpdateDTO.oldPassword != null) { IHashingAlgorithm? userHashingAlgorithm = _hashingFactory.getAlgorithm(destUser.hashingType); if (userHashingAlgorithm == null) { _logger.Log(LogLevel.Warning, "User id '{id}' has a hashing type '{hashingType}' that isn't recognized by factory '{factory}'. Not logging in.", destUser.id, destUser.hashingType, nameof(_hashingFactory)); return null; } string hashedOldPassword = userHashingAlgorithm.hash(userPasswordUpdateDTO.oldPassword, destUser.salt); if (hashedOldPassword.Equals(destUser.password)) { oldPasswordMatchNew = true; } } User? updatedUser = _userService.changePassword(destUser, changingUser, hashedNewPassword, newSalt, oldPasswordMatchNew); if (updatedUser == null) return null; UserDTO dto = new UserDTO(); dto.adaptFromModel(updatedUser); return dto; } } }