You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

870 lines
30 KiB
C#

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<AuthResult> LoginAsync(LoginRequest request);
Task<bool> LogoutAsync(int userId);
Task<bool> ValidateTokenAsync(string token);
Task<UserClaims> GetUserClaimsAsync(int userId);
Task<string> GenerateRefreshTokenAsync(int userId);
Task<bool> ValidateRefreshTokenAsync(int userId, string refreshToken);
Task<AuthResult> RefreshTokenAsync(string refreshToken);
}
public interface IUserService
{
Task<UserViewModel> CreateUserAsync(User user);
Task<UserViewModel> UpdateUserAsync(int userId, User user);
Task<bool> DeleteUserAsync(int userId);
Task<UserViewModel> GetUserByIdAsync(int userId);
Task<IEnumerable<UserViewModel>> GetAllUsersAsync();
Task<IEnumerable<UserViewModel>> GetUsersByRoleAsync(string roleName);
Task<bool> ActivateUserAsync(int userId);
Task<bool> DeactivateUserAsync(int userId);
Task<bool> ChangePasswordAsync(int userId, string oldPassword, string newPassword);
Task<bool> ResetPasswordAsync(int userId, string newPassword);
Task<bool> AssignRoleAsync(int userId, int roleId);
Task<bool> UnassignRoleAsync(int userId, int roleId);
}
public interface IPermissionService
{
Task<IEnumerable<Permission>> GetAllPermissionsAsync();
Task<Permission> GetPermissionByIdAsync(int permissionId);
Task<IEnumerable<Permission>> GetPermissionsByCategoryAsync(string category);
Task<bool> UserHasPermissionAsync(int userId, string permissionName);
Task<IEnumerable<string>> GetUserPermissionsAsync(int userId);
Task<bool> AddPermissionToRoleAsync(int roleId, int permissionId);
Task<bool> RemovePermissionFromRoleAsync(int roleId, int permissionId);
Task<bool> CreatePermissionAsync(Permission permission);
Task<bool> UpdatePermissionAsync(Permission permission);
Task<bool> DeletePermissionAsync(int permissionId);
}
public interface ISessionService
{
Task<UserSession> CreateSessionAsync(int userId, string deviceInfo, string ipAddress);
Task<bool> ValidateSessionAsync(string sessionToken);
Task<UserSession> GetSessionByTokenAsync(string sessionToken);
Task<bool> UpdateSessionActivityAsync(string sessionToken);
Task<bool> TerminateSessionAsync(string sessionToken);
Task<bool> TerminateAllUserSessionsAsync(int userId);
Task<IEnumerable<UserSession>> GetUserSessionsAsync(int userId);
Task<bool> 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> _jwtSettings;
private readonly ILoggerService _logger;
public AuthService(
IUserRepository userRepository,
IRoleRepository roleRepository,
IUserSessionRepository userSessionRepository,
IPasswordResetRepository passwordResetRepository,
IOptions<JwtSettings> jwtSettings,
ILoggerService logger)
{
_userRepository = userRepository;
_roleRepository = roleRepository;
_userSessionRepository = userSessionRepository;
_passwordResetRepository = passwordResetRepository;
_jwtSettings = jwtSettings;
_logger = logger;
}
public async Task<AuthResult> 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<bool> 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<bool> 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<UserClaims> 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<string> 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<bool> ValidateRefreshTokenAsync(int userId, string refreshToken)
{
var session = await _userSessionRepository.GetSessionByTokenAsync(refreshToken);
return session != null && session.UserId == userId && session.IsActive;
}
public async Task<AuthResult> 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<UserViewModel> 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<UserViewModel> 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<bool> 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<UserViewModel> 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<IEnumerable<UserViewModel>> GetAllUsersAsync()
{
var users = await _userRepository.GetAllAsync();
var userViewModels = new List<UserViewModel>();
foreach (var user in users)
{
userViewModels.Add(await GetUserByIdAsync(user.Id));
}
return userViewModels;
}
public async Task<IEnumerable<UserViewModel>> GetUsersByRoleAsync(string roleName)
{
var role = await _roleRepository.GetByNameAsync(roleName);
if (role == null)
return new List<UserViewModel>();
var users = await _userRepository.GetByRoleIdAsync(role.Id);
var userViewModels = new List<UserViewModel>();
foreach (var user in users)
{
userViewModels.Add(await GetUserByIdAsync(user.Id));
}
return userViewModels;
}
public async Task<bool> ActivateUserAsync(int userId)
{
return await SetUserActiveStatusAsync(userId, true);
}
public async Task<bool> DeactivateUserAsync(int userId)
{
return await SetUserActiveStatusAsync(userId, false);
}
public async Task<bool> 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<bool> 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<bool> 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<bool> 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<bool> 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<IEnumerable<Permission>> GetAllPermissionsAsync()
{
return await _permissionRepository.GetAllAsync();
}
public async Task<Permission> GetPermissionByIdAsync(int permissionId)
{
return await _permissionRepository.GetByIdAsync(permissionId);
}
public async Task<IEnumerable<Permission>> GetPermissionsByCategoryAsync(string category)
{
return await _permissionRepository.FindAsync(p => p.Category == category);
}
public async Task<bool> UserHasPermissionAsync(int userId, string permissionName)
{
return await _roleRepository.UserHasPermissionAsync(userId, permissionName);
}
public async Task<IEnumerable<string>> GetUserPermissionsAsync(int userId)
{
var user = await _roleRepository.GetUserById(userId); // Assuming this method exists
if (user == null)
return new List<string>();
var role = await _roleRepository.GetByIdAsync(user.RoleId);
return role?.Permissions ?? new List<string>();
}
public async Task<bool> 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<bool> 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<bool> 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<bool> 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<bool> 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<UserSession> 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<bool> ValidateSessionAsync(string sessionToken)
{
try
{
var session = await _userSessionRepository.GetSessionByTokenAsync(sessionToken);
return session != null && session.IsActive;
}
catch
{
return false;
}
}
public async Task<UserSession> GetSessionByTokenAsync(string sessionToken)
{
return await _userSessionRepository.GetSessionByTokenAsync(sessionToken);
}
public async Task<bool> 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<bool> 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<bool> 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<IEnumerable<UserSession>> GetUserSessionsAsync(int userId)
{
return await _userSessionRepository.GetUserSessionsAsync(userId);
}
public async Task<bool> 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;
}
}
}
}