using AutoMapper; using AutoMapper.QueryableExtensions; using LANCommander.Server.Data; using LANCommander.Server.Data.Enums; using LANCommander.Server.Data.Models; using LANCommander.Server.Services.Factories; using LANCommander.Server.Services.Models; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using System.Linq.Expressions; using LANCommander.Server.Services.Exceptions; using LANCommander.Server.Services.Extensions; using ZiggyCreatures.Caching.Fusion; namespace LANCommander.Server.Services { public class UserService : BaseService, IBaseDatabaseService { private readonly IdentityContext IdentityContext; private readonly CollectionService CollectionService; private readonly IMapper Mapper; private readonly IFusionCache Cache; public UserService( ILogger logger, IMapper mapper, IFusionCache cache, CollectionService collectionService, IdentityContextFactory identityContextFactory) : base(logger) { IdentityContext = identityContextFactory.Create(); CollectionService = collectionService; Mapper = mapper; Cache = cache; } public async Task GetAsync(string userName) { return await IdentityContext.UserManager.FindByNameAsync(userName); } public async Task GetAsync(string userName) { var user = await IdentityContext.UserManager.FindByNameAsync(userName); return Mapper.Map(user); } public async Task> GetRolesAsync(User user) { var roles = await Cache.GetOrSetAsync($"User/{user.Id}/Roles", async _ => { try { user = await IdentityContext.UserManager.FindByIdAsync(user.Id.ToString()); var roleNames = await IdentityContext.UserManager.GetRolesAsync(user); return await IdentityContext.RoleManager.Roles.Where(r => roleNames.Contains(r.Name)).ToListAsync(); } catch (Exception ex) { _logger.LogError(ex, "Could not get roles for user {Username}", user.UserName); return new List(); } }, tags: ["User/Security", "User/Roles", $"User/{user.Id}"]); return roles; } public async Task IsInRoleAsync(User user, string roleName) { var roles = await GetRolesAsync(user); return roles.Any(r => r.Name == roleName); } public async Task> GetCollectionsAsync(User user) { try { var roles = await GetRolesAsync(user); var roleIds = roles.Select(r => r.Id).ToList(); if (roles.Any(r => r.Name.Equals(RoleService.AdministratorRoleName, StringComparison.OrdinalIgnoreCase))) return await CollectionService.GetAsync(); else return await CollectionService .Include(c => c.Roles) .GetAsync(c => c.Roles.Any(r => roleIds.Contains(r.Id))); } catch (Exception ex) { _logger.LogError(ex, "Could not get collections for user {UserName}", user.UserName); return new List(); } } public async Task AddAsync(User user) { var result = await IdentityContext.UserManager.CreateAsync(user); if (result.Succeeded) return await IdentityContext.UserManager.FindByNameAsync(user.UserName); else throw new UserRegistrationException(result, "Could not create user"); } public async Task AddToRoleAsync(string userName, string roleName) { var user = await IdentityContext.UserManager.FindByNameAsync(userName); await IdentityContext.UserManager.AddToRoleAsync(user, roleName); } public async Task AddToRolesAsync(string userName, IEnumerable roleNames) { var user = await IdentityContext.UserManager.FindByNameAsync(userName); var result = await IdentityContext.UserManager.AddToRolesAsync(user, roleNames); await Cache.RemoveByTagAsync(["User/Security", "User/Roles", $"User/{user.Id}", $"Library/{user.Id}"]); if (!result.Succeeded) throw new AddRoleException(result, "Could not add roles"); } public async Task RemoveFromRole(string userName, string roleName) { var user = await IdentityContext.UserManager.FindByNameAsync(userName); await IdentityContext.UserManager.RemoveFromRoleAsync(user, roleName); } public async Task CheckPassword(string userName, string password) { var user = await IdentityContext.UserManager.FindByNameAsync(userName); return await IdentityContext.UserManager.CheckPasswordAsync(user, password); } public async Task ChangePassword(string userName, string currentPassword, string newPassword) { var user = await IdentityContext.UserManager.FindByNameAsync(userName); var result = await IdentityContext.UserManager.ChangePasswordAsync(user, currentPassword, newPassword); return result; } public async Task ChangePassword(string userName, string newPassword) { var user = await IdentityContext.UserManager.FindByNameAsync(userName); var token = await IdentityContext.UserManager.GeneratePasswordResetTokenAsync(user); return await IdentityContext.UserManager.ResetPasswordAsync(user, token, newPassword); } public async Task SignOut() { } public IBaseDatabaseService AsSplitQuery() { throw new NotImplementedException(); } public async Task> GetAsync() { return await IdentityContext.UserManager.Users.ToListAsync(); } public async Task> GetAsync() { return await IdentityContext .UserManager .Users .ProjectTo(Mapper.ConfigurationProvider) .ToListAsync(); } public async Task GetAsync(Guid id) { return await IdentityContext .UserManager .FindByIdAsync(id.ToString()); } public async Task GetAsync(Guid id) { var user = await IdentityContext .UserManager .FindByIdAsync(id.ToString()); return Mapper.Map(user); } public async Task> GetAsync(Expression> predicate) { return await IdentityContext .UserManager .Users .Where(predicate) .ToListAsync(); } public async Task> GetAsync(Expression> predicate) { return await IdentityContext .UserManager .Users .Where(predicate) .ProjectTo(Mapper.ConfigurationProvider) .ToListAsync(); } public async Task FirstOrDefaultAsync(Expression> predicate) { return await IdentityContext .UserManager .Users .FirstOrDefaultAsync(predicate); } public async Task FirstOrDefaultAsync(Expression> predicate) { return await IdentityContext .UserManager .Users .Where(predicate) .ProjectTo(Mapper.ConfigurationProvider) .FirstOrDefaultAsync(); } public async Task FirstOrDefaultAsync(Expression> predicate, Expression> orderKeySelector) { return await IdentityContext .UserManager .Users .Where(predicate) .OrderBy(orderKeySelector) .FirstOrDefaultAsync(); } public async Task FirstOrDefaultAsync(Expression> predicate, Expression> orderKeySelector) { return await IdentityContext .UserManager .Users .Where(predicate) .ProjectTo(Mapper.ConfigurationProvider) .OrderBy(orderKeySelector) .FirstOrDefaultAsync(); } public async Task ExistsAsync(Guid id) { var user = await IdentityContext .UserManager .FindByIdAsync(id.ToString()); return user != null; } public async Task> AddMissingAsync(Expression> predicate, User entity) { var result = new ExistingEntityResult(); var user = await IdentityContext .UserManager .Users .FirstOrDefaultAsync(predicate); if (user == null) { await IdentityContext.UserManager.CreateAsync(entity); result.Existing = false; result.Value = await IdentityContext.UserManager.FindByNameAsync(user.UserName); } else { result.Existing = true; result.Value = user; } return result; } public async Task UpdateAsync(User entity) { var user = await IdentityContext .UserManager .FindByIdAsync(entity.Id.ToString()); user.UserName = entity.UserName; user.PhoneNumber = entity.PhoneNumber; user.Email = entity.Email; user.TwoFactorEnabled = entity.TwoFactorEnabled; user.Alias = entity.Alias; user.Approved = entity.Approved; user.ApprovedOn = entity.ApprovedOn; await IdentityContext.UserManager.UpdateAsync(user); return user; } public async Task DeleteAsync(User entity) { var user = await IdentityContext .UserManager .FindByIdAsync(entity.Id.ToString()); await IdentityContext.UserManager.DeleteAsync(user); } public IBaseDatabaseService Include(Expression> includeExpression) { throw new NotImplementedException(); } public IBaseDatabaseService Query(Func, IQueryable> modifier) { throw new NotImplementedException(); } public IBaseDatabaseService Include(params Expression>[] expressions) { throw new NotImplementedException(); } public IBaseDatabaseService SortBy(Expression> expression, SortDirection direction) { throw new NotImplementedException(); } public IBaseDatabaseService AsNoTracking() { throw new NotImplementedException(); } public Task> PaginateAsync(Expression> expression, int pageNumber, int pageSize) { throw new NotImplementedException(); } public void Dispose() { } } }