using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Security.Cryptography; using System.Text; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using Haoliang.Models.User; using Haoliang.Data.Repositories; using Haoliang.Core.Services; namespace Haoliang.Core.Services { public interface IAuthService { Task LoginAsync(LoginRequest request); Task LogoutAsync(int userId); Task ValidateTokenAsync(string token); Task GetUserClaimsAsync(int userId); Task GenerateRefreshTokenAsync(int userId); Task ValidateRefreshTokenAsync(int userId, string refreshToken); Task RefreshTokenAsync(string refreshToken); } public interface IUserService { Task CreateUserAsync(User user); Task UpdateUserAsync(int userId, User user); Task DeleteUserAsync(int userId); Task GetUserByIdAsync(int userId); Task> GetAllUsersAsync(); Task> GetUsersByRoleAsync(string roleName); Task ActivateUserAsync(int userId); Task DeactivateUserAsync(int userId); Task ChangePasswordAsync(int userId, string oldPassword, string newPassword); Task ResetPasswordAsync(int userId, string newPassword); Task AssignRoleAsync(int userId, int roleId); Task UnassignRoleAsync(int userId, int roleId); } public interface IPermissionService { Task> GetAllPermissionsAsync(); Task GetPermissionByIdAsync(int permissionId); Task> GetPermissionsByCategoryAsync(string category); Task UserHasPermissionAsync(int userId, string permissionName); Task> GetUserPermissionsAsync(int userId); Task AddPermissionToRoleAsync(int roleId, int permissionId); Task RemovePermissionFromRoleAsync(int roleId, int permissionId); Task CreatePermissionAsync(Permission permission); Task UpdatePermissionAsync(Permission permission); Task DeletePermissionAsync(int permissionId); } public interface ISessionService { Task CreateSessionAsync(int userId, string deviceInfo, string ipAddress); Task ValidateSessionAsync(string sessionToken); Task GetSessionByTokenAsync(string sessionToken); Task UpdateSessionActivityAsync(string sessionToken); Task TerminateSessionAsync(string sessionToken); Task TerminateAllUserSessionsAsync(int userId); Task> GetUserSessionsAsync(int userId); Task CleanupExpiredSessionsAsync(); } public class JwtSettings { public string SecretKey { get; set; } public string Issuer { get; set; } public string Audience { get; set; } public int AccessTokenExpirationMinutes { get; set; } public int RefreshTokenExpirationDays { get; set; } } public class AuthService : IAuthService { private readonly IUserRepository _userRepository; private readonly IRoleRepository _roleRepository; private readonly IUserSessionRepository _userSessionRepository; private readonly IPasswordResetRepository _passwordResetRepository; private readonly IOptions _jwtSettings; private readonly ILoggerService _logger; public AuthService( IUserRepository userRepository, IRoleRepository roleRepository, IUserSessionRepository userSessionRepository, IPasswordResetRepository passwordResetRepository, IOptions jwtSettings, ILoggerService logger) { _userRepository = userRepository; _roleRepository = roleRepository; _userSessionRepository = userSessionRepository; _passwordResetRepository = passwordResetRepository; _jwtSettings = jwtSettings; _logger = logger; } public async Task LoginAsync(LoginRequest request) { try { var user = await _userRepository.AuthenticateAsync(request.Username, request.Password); if (user == null) { await _logger.LogWarningAsync($"Failed login attempt for username: {request.Username}"); return new AuthResult { Success = false, Message = "Invalid username or password" }; } var token = GenerateJwtToken(user); var refreshToken = await GenerateRefreshTokenAsync(user.Id); var userClaims = await GetUserClaimsAsync(user.Id); var permissions = await _roleRepository.GetRolePermissionsAsync(user.RoleId); await _logger.LogInformationAsync($"User {user.Username} logged in successfully"); return new AuthResult { Success = true, Token = token, User = user, Permissions = permissions.Select(p => p.Name).ToList(), ExpiresAt = DateTime.Now.AddMinutes(_jwtSettings.Value.AccessTokenExpirationMinutes) }; } catch (Exception ex) { await _logger.LogErrorAsync($"Login failed: {ex.Message}"); return new AuthResult { Success = false, Message = "An error occurred during login" }; } } public async Task LogoutAsync(int userId) { try { var sessions = await _userSessionRepository.GetUserSessionsAsync(userId); foreach (var session in sessions) { await _userSessionRepository.TerminateSessionAsync(session.SessionToken); } await _logger.LogInformationAsync($"User {userId} logged out"); return true; } catch (Exception ex) { await _logger.LogErrorAsync($"Logout failed: {ex.Message}"); return false; } } public async Task ValidateTokenAsync(string token) { try { var tokenHandler = new JwtSecurityTokenHandler(); var key = Encoding.UTF8.GetBytes(_jwtSettings.Value.SecretKey); tokenHandler.ValidateToken(token, new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(key), ValidateIssuer = true, ValidIssuer = _jwtSettings.Value.Issuer, ValidateAudience = true, ValidAudience = _jwtSettings.Value.Audience, ValidateLifetime = true, ClockSkew = TimeSpan.Zero }, out SecurityToken validatedToken); return true; } catch { return false; } } public async Task GetUserClaimsAsync(int userId) { var user = await _userRepository.GetByIdAsync(userId); if (user == null) return null; var role = await _roleRepository.GetByIdAsync(user.RoleId); var permissions = await _roleRepository.GetRolePermissionsAsync(user.RoleId); return new UserClaims { UserId = user.Id, Username = user.Username, RealName = user.RealName, RoleId = user.RoleId, RoleName = role?.RoleName ?? "", Permissions = permissions.Select(p => p.Name).ToList(), SessionTime = DateTime.Now }; } public async Task GenerateRefreshTokenAsync(int userId) { var refreshToken = Guid.NewGuid().ToString(); var expiresAt = DateTime.Now.AddDays(_jwtSettings.Value.RefreshTokenExpirationDays); var session = new UserSession { UserId = userId, SessionToken = refreshToken, DeviceInfo = "", IPAddress = "", LoginTime = DateTime.Now, LastActivityTime = DateTime.Now, IsActive = true, CreatedAt = DateTime.Now }; await _userSessionRepository.AddAsync(session); await _userSessionRepository.SaveAsync(); return refreshToken; } public async Task ValidateRefreshTokenAsync(int userId, string refreshToken) { var session = await _userSessionRepository.GetSessionByTokenAsync(refreshToken); return session != null && session.UserId == userId && session.IsActive; } public async Task RefreshTokenAsync(string refreshToken) { try { var session = await _userSessionRepository.GetSessionByTokenAsync(refreshToken); if (session == null || !session.IsActive) { return new AuthResult { Success = false, Message = "Invalid refresh token" }; } var user = await _userRepository.GetByIdAsync(session.UserId); if (user == null) { return new AuthResult { Success = false, Message = "User not found" }; } var newToken = GenerateJwtToken(user); var newRefreshToken = await GenerateRefreshTokenAsync(user.Id); // Terminate old refresh token await _userSessionRepository.TerminateSessionAsync(refreshToken); var userClaims = await GetUserClaimsAsync(user.Id); var permissions = await _roleRepository.GetRolePermissionsAsync(user.RoleId); return new AuthResult { Success = true, Token = newToken, RefreshToken = newRefreshToken, User = user, Permissions = permissions.Select(p => p.Name).ToList(), ExpiresAt = DateTime.Now.AddMinutes(_jwtSettings.Value.AccessTokenExpirationMinutes) }; } catch (Exception ex) { await _logger.LogErrorAsync($"Token refresh failed: {ex.Message}"); return new AuthResult { Success = false, Message = "Failed to refresh token" }; } } private string GenerateJwtToken(User user) { var tokenHandler = new JwtSecurityTokenHandler(); var key = Encoding.UTF8.GetBytes(_jwtSettings.Value.SecretKey); var claims = new[] { new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), new Claim(ClaimTypes.Name, user.Username), new Claim(ClaimTypes.GivenName, user.RealName), new Claim(ClaimTypes.Email, user.Email), new Claim(ClaimTypes.Role, user.RoleId.ToString()) }; var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(claims), Expires = DateTime.Now.AddMinutes(_jwtSettings.Value.AccessTokenExpirationMinutes), Issuer = _jwtSettings.Value.Issuer, Audience = _jwtSettings.Value.Audience, SigningCredentials = new SigningCredentials( new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) }; var token = tokenHandler.CreateToken(tokenDescriptor); return tokenHandler.WriteToken(token); } } public class UserService : IUserService { private readonly IUserRepository _userRepository; private readonly IRoleRepository _roleRepository; private readonly IEmployeeRepository _employeeRepository; private readonly IPermissionService _permissionService; private readonly ILoggerService _logger; public UserService( IUserRepository userRepository, IRoleRepository roleRepository, IEmployeeRepository employeeRepository, IPermissionService permissionService, ILoggerService logger) { _userRepository = userRepository; _roleRepository = roleRepository; _employeeRepository = employeeRepository; _permissionService = permissionService; _logger = logger; } public async Task CreateUserAsync(User user) { try { if (await _userRepository.UsernameExistsAsync(user.Username)) { throw new Exception("Username already exists"); } if (await _userRepository.EmailExistsAsync(user.Email)) { throw new Exception("Email already exists"); } user.PasswordHash = HashPassword(user.PasswordHash); user.IsActive = true; user.CreatedAt = DateTime.Now; user.UpdatedAt = DateTime.Now; await _userRepository.AddAsync(user); await _userRepository.SaveAsync(); await _logger.LogInformationAsync($"User created: {user.Username}"); return await GetUserByIdAsync(user.Id); } catch (Exception ex) { await _logger.LogErrorAsync($"Failed to create user: {ex.Message}"); throw; } } public async Task UpdateUserAsync(int userId, User user) { try { var existingUser = await _userRepository.GetByIdAsync(userId); if (existingUser == null) throw new Exception("User not found"); // Don't update username if it hasn't changed if (existingUser.Username != user.Username) { if (await _userRepository.UsernameExistsAsync(user.Username)) throw new Exception("Username already exists"); } // Don't update email if it hasn't changed if (existingUser.Email != user.Email) { if (await _userRepository.EmailExistsAsync(user.Email)) throw new Exception("Email already exists"); } // Update user properties existingUser.RealName = user.RealName; existingUser.Email = user.Email; existingUser.Phone = user.Phone; existingUser.RoleId = user.RoleId; existingUser.IsActive = user.IsActive; existingUser.UpdatedAt = DateTime.Now; if (!string.IsNullOrEmpty(user.PasswordHash)) { existingUser.PasswordHash = HashPassword(user.PasswordHash); } _userRepository.Update(existingUser); await _userRepository.SaveAsync(); await _logger.LogInformationAsync($"User updated: {existingUser.Username}"); return await GetUserByIdAsync(userId); } catch (Exception ex) { await _logger.LogErrorAsync($"Failed to update user: {ex.Message}"); throw; } } public async Task DeleteUserAsync(int userId) { try { var user = await _userRepository.GetByIdAsync(userId); if (user == null) return false; // Check if user has active sessions // (Assuming you have a session repository) // Check if user is assigned to devices var employee = await _employeeRepository.GetByEmployeeCodeAsync(user.Username); if (employee != null && employee.AssignedDevices.Any()) { throw new Exception("Cannot delete user that is assigned to devices"); } _userRepository.Remove(user); await _userRepository.SaveAsync(); await _logger.LogInformationAsync($"User deleted: {user.Username}"); return true; } catch (Exception ex) { await _logger.LogErrorAsync($"Failed to delete user: {ex.Message}"); throw; } } public async Task GetUserByIdAsync(int userId) { var user = await _userRepository.GetByIdAsync(userId); if (user == null) return null; var role = await _roleRepository.GetByIdAsync(user.RoleId); var permissions = await _roleRepository.GetRolePermissionsAsync(user.RoleId); return new UserViewModel { Id = user.Id, Username = user.Username, RealName = user.RealName, Email = user.Email, Phone = user.Phone, RoleName = role?.RoleName ?? "", IsActive = user.IsActive, LastLoginTime = user.LastLoginTime, CreatedAt = user.CreatedAt, Permissions = permissions.Select(p => p.Name).ToList() }; } public async Task> GetAllUsersAsync() { var users = await _userRepository.GetAllAsync(); var userViewModels = new List(); foreach (var user in users) { userViewModels.Add(await GetUserByIdAsync(user.Id)); } return userViewModels; } public async Task> GetUsersByRoleAsync(string roleName) { var role = await _roleRepository.GetByNameAsync(roleName); if (role == null) return new List(); var users = await _userRepository.GetByRoleIdAsync(role.Id); var userViewModels = new List(); foreach (var user in users) { userViewModels.Add(await GetUserByIdAsync(user.Id)); } return userViewModels; } public async Task ActivateUserAsync(int userId) { return await SetUserActiveStatusAsync(userId, true); } public async Task DeactivateUserAsync(int userId) { return await SetUserActiveStatusAsync(userId, false); } public async Task ChangePasswordAsync(int userId, string oldPassword, string newPassword) { try { return await _userRepository.ChangePasswordAsync(userId, oldPassword, newPassword); } catch (Exception ex) { await _logger.LogErrorAsync($"Failed to change password: {ex.Message}"); return false; } } public async Task ResetPasswordAsync(int userId, string newPassword) { try { return await _userRepository.ResetPasswordAsync(userId, newPassword); } catch (Exception ex) { await _logger.LogErrorAsync($"Failed to reset password: {ex.Message}"); return false; } } public async Task AssignRoleAsync(int userId, int roleId) { var user = await _userRepository.GetByIdAsync(userId); if (user == null) return false; user.RoleId = roleId; user.UpdatedAt = DateTime.Now; _userRepository.Update(user); await _userRepository.SaveAsync(); return true; } public async Task UnassignRoleAsync(int userId, int roleId) { var user = await _userRepository.GetByIdAsync(userId); if (user == null || user.RoleId != roleId) return false; // Assign default role var defaultRole = await _roleRepository.GetByNameAsync("User"); if (defaultRole != null) { user.RoleId = defaultRole.Id; user.UpdatedAt = DateTime.Now; _userRepository.Update(user); await _userRepository.SaveAsync(); return true; } return false; } private async Task SetUserActiveStatusAsync(int userId, bool isActive) { var user = await _userRepository.GetByIdAsync(userId); if (user == null) return false; user.IsActive = isActive; user.UpdatedAt = DateTime.Now; _userRepository.Update(user); await _userRepository.SaveAsync(); return true; } private string HashPassword(string password) { using (var sha256 = SHA256.Create()) { var bytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(password)); return Convert.ToBase64String(bytes); } } } public class PermissionService : IPermissionService { private readonly IRoleRepository _roleRepository; private readonly IPermissionRepository _permissionRepository; private readonly ILoggerService _logger; public PermissionService( IRoleRepository roleRepository, IPermissionRepository permissionRepository, ILoggerService logger) { _roleRepository = roleRepository; _permissionRepository = permissionRepository; _logger = logger; } public async Task> GetAllPermissionsAsync() { return await _permissionRepository.GetAllAsync(); } public async Task GetPermissionByIdAsync(int permissionId) { return await _permissionRepository.GetByIdAsync(permissionId); } public async Task> GetPermissionsByCategoryAsync(string category) { return await _permissionRepository.FindAsync(p => p.Category == category); } public async Task UserHasPermissionAsync(int userId, string permissionName) { return await _roleRepository.UserHasPermissionAsync(userId, permissionName); } public async Task> GetUserPermissionsAsync(int userId) { var user = await _roleRepository.GetUserById(userId); // Assuming this method exists if (user == null) return new List(); var role = await _roleRepository.GetByIdAsync(user.RoleId); return role?.Permissions ?? new List(); } public async Task AddPermissionToRoleAsync(int roleId, int permissionId) { try { return await _roleRepository.AddPermissionToRoleAsync(roleId, permissionId); } catch (Exception ex) { await _logger.LogErrorAsync($"Failed to add permission to role: {ex.Message}"); return false; } } public async Task RemovePermissionFromRoleAsync(int roleId, int permissionId) { try { return await _roleRepository.RemovePermissionFromRoleAsync(roleId, permissionId); } catch (Exception ex) { await _logger.LogErrorAsync($"Failed to remove permission from role: {ex.Message}"); return false; } } public async Task CreatePermissionAsync(Permission permission) { try { return await _permissionRepository.AddPermissionAsync(permission); } catch (Exception ex) { await _logger.LogErrorAsync($"Failed to create permission: {ex.Message}"); return false; } } public async Task UpdatePermissionAsync(Permission permission) { try { await _permissionRepository.UpdatePermissionAsync(permission); return true; } catch (Exception ex) { await _logger.LogErrorAsync($"Failed to update permission: {ex.Message}"); return false; } } public async Task DeletePermissionAsync(int permissionId) { try { return await _permissionRepository.DeletePermissionAsync(permissionId); } catch (Exception ex) { await _logger.LogErrorAsync($"Failed to delete permission: {ex.Message}"); return false; } } } public class SessionService : ISessionService { private readonly IUserSessionRepository _userSessionRepository; private readonly ILoggerService _logger; public SessionService( IUserSessionRepository userSessionRepository, ILoggerService logger) { _userSessionRepository = userSessionRepository; _logger = logger; } public async Task CreateSessionAsync(int userId, string deviceInfo, string ipAddress) { var sessionToken = Guid.NewGuid().ToString(); var session = new UserSession { UserId = userId, SessionToken = sessionToken, DeviceInfo = deviceInfo, IPAddress = ipAddress, LoginTime = DateTime.Now, LastActivityTime = DateTime.Now, IsActive = true, CreatedAt = DateTime.Now }; await _userSessionRepository.AddAsync(session); await _userSessionRepository.SaveAsync(); await _logger.LogInformationAsync($"Session created for user {userId}"); return session; } public async Task ValidateSessionAsync(string sessionToken) { try { var session = await _userSessionRepository.GetSessionByTokenAsync(sessionToken); return session != null && session.IsActive; } catch { return false; } } public async Task GetSessionByTokenAsync(string sessionToken) { return await _userSessionRepository.GetSessionByTokenAsync(sessionToken); } public async Task UpdateSessionActivityAsync(string sessionToken) { try { var session = await _userSessionRepository.GetSessionByTokenAsync(sessionToken); if (session != null) { session.LastActivityTime = DateTime.Now; await _userSessionRepository.UpdateSessionAsync(session); return true; } return false; } catch (Exception ex) { await _logger.LogErrorAsync($"Failed to update session: {ex.Message}"); return false; } } public async Task TerminateSessionAsync(string sessionToken) { try { var session = await _userSessionRepository.GetSessionByTokenAsync(sessionToken); if (session != null) { session.IsActive = false; session.LogoutTime = DateTime.Now; await _userSessionRepository.UpdateSessionAsync(session); return true; } return false; } catch (Exception ex) { await _logger.LogErrorAsync($"Failed to terminate session: {ex.Message}"); return false; } } public async Task TerminateAllUserSessionsAsync(int userId) { try { var sessions = await _userSessionRepository.GetUserSessionsAsync(userId); foreach (var session in sessions) { await TerminateSessionAsync(session.SessionToken); } return true; } catch (Exception ex) { await _logger.LogErrorAsync($"Failed to terminate all sessions: {ex.Message}"); return false; } } public async Task> GetUserSessionsAsync(int userId) { return await _userSessionRepository.GetUserSessionsAsync(userId); } public async Task CleanupExpiredSessionsAsync() { try { var expiredSessions = await _userSessionRepository.GetExpiredSessionsAsync(); if (expiredSessions.Any()) { await _userSessionRepository.RemoveRangeAsync(expiredSessions); await _userSessionRepository.SaveAsync(); await _logger.LogInformationAsync($"Cleaned up {expiredSessions.Count()} expired sessions"); return true; } return false; } catch (Exception ex) { await _logger.LogErrorAsync($"Failed to cleanup expired sessions: {ex.Message}"); return false; } } } }