using System.Diagnostics; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using AsbCloudApp.Data; using AsbCloudDb.Model; using AsbCloudInfrastructure.Services.Cache; using Mapster; using AsbCloudApp.Services; using System; namespace AsbCloudInfrastructure.Services { public class UserRoleService : IUserRoleService { private readonly CacheTable cacheUserRoles; private readonly CacheTable cachePermission; private readonly IPermissionService permissionService; public List Includes { get; } = new(); public UserRoleService(IAsbCloudDbContext context, CacheDb cacheDb, IPermissionService permissionService) { cacheUserRoles = cacheDb.GetCachedTable((AsbCloudDbContext)context, new [] { nameof(UserRole.RelationUserRolePermissions) }); cachePermission = cacheDb.GetCachedTable((AsbCloudDbContext)context); this.permissionService = permissionService; } public async Task InsertAsync(UserRoleDto dto, CancellationToken token = default) { var entity = dto.Adapt(); var updatedEntity = await cacheUserRoles.InsertAsync(entity, token) .ConfigureAwait(false); await UpdatePermissionsAsync(dto, token); await cacheUserRoles.RefreshAsync(true, token) .ConfigureAwait(false); return updatedEntity?.Id ?? 0; } public Task InsertRangeAsync(IEnumerable dtos, CancellationToken token = default) { throw new NotImplementedException(); //var entities = dtos.Adapt(); //return await cacheUserRoles.InsertAsync(entities, token).ConfigureAwait(false); } public async Task> GetAllAsync(CancellationToken token = default) { var entities = await cacheUserRoles.WhereAsync(token) .ConfigureAwait(false); var dtos = entities.Adapt(); return dtos; } public async Task GetAsync(int id, CancellationToken token = default) { var entity = await cacheUserRoles.FirstOrDefaultAsync(r=>r.Id == id, token) .ConfigureAwait(false); var dto = entity?.Adapt(); return dto; } public async Task GetByNameAsync(string name, CancellationToken token = default) { var entity = await cacheUserRoles.FirstOrDefaultAsync(r => r.Caption == name, token) .ConfigureAwait(false); var dto = entity?.Adapt(); return dto; } public async Task UpdateAsync(int id, UserRoleDto dto, CancellationToken token = default) { dto.Id = id; var entity = dto.Adapt(); await UpdatePermissionsAsync(dto, token); await cacheUserRoles.UpsertAsync(entity, token) .ConfigureAwait(false); return id; } public List GetNestedById(int id, int recursionLevel = 7) { var role = cacheUserRoles.FirstOrDefault(r => r.Id == id); if (role is null) return null; var dto = role.Adapt(); if (role.IdParent is null || recursionLevel == 0) return new List { dto }; var parentRoles = GetNestedById((int)role.IdParent, --recursionLevel) ?? new List(); parentRoles.Add(dto); return parentRoles; } public IEnumerable GetNestedPermissions(IEnumerable roles) { var permissions = new Dictionary(16); foreach (var roleDto in roles) { var role = cacheUserRoles.FirstOrDefault(r => r.Id == roleDto.Id); var rolePermissions = GetNestedPermissions(role, 10); if ((rolePermissions?.Any()) != true) continue; foreach (var newPermission in rolePermissions) { if (permissions.ContainsKey(newPermission.Id)) { permissions[newPermission.Id] = newPermission.Adapt(); } else { permissions.Add(newPermission.Id, new PermissionBaseDto { Id = newPermission.Id, Name = newPermission.Name ?? cachePermission.FirstOrDefault(p => p.Id == newPermission.Id).Name }); } } } return permissions.Values; } private async Task UpdatePermissionsAsync(UserRoleDto roleDto, CancellationToken token) { await permissionService.DeleteAllByRoleAsync(roleDto.Id, token) .ConfigureAwait(false); if (!roleDto.Permissions.Any()) return; var newPermissions = roleDto.Permissions.Select(p => new PermissionDto { Id = p.Id, IdUserRole = roleDto.Id, Name = p.Name }); await permissionService.InsertRangeAsync(newPermissions, token) .ConfigureAwait(false); } private IEnumerable GetNestedPermissions(UserRole role, int recursionLevel = 7) { var permissionRelations = role.RelationUserRolePermissions.ToList(); var permissionIds = permissionRelations.Select(p => p.IdPermission); if (role.IdParent is null) return cachePermission.Where(c => permissionIds.Contains(c.Id)); if (recursionLevel == 0) { Trace.WriteLine($"User role with id: {role.Id} has more than 10 nested children"); cachePermission.Where(c => permissionIds.Contains(c.Id)); } var parentRole = cacheUserRoles.FirstOrDefault(r => r.Id == role.IdParent); if (parentRole is null) return cachePermission.Where(c => permissionIds.Contains(c.Id)); var parentPermissions = GetNestedPermissions(parentRole, --recursionLevel); return cachePermission.Where(c => permissionIds.Contains(c.Id)).Union(parentPermissions); } public Task DeleteAsync(int id, CancellationToken token = default) => cacheUserRoles.RemoveAsync(r => r.Id == id, token); public Task DeleteAsync(IEnumerable ids, CancellationToken token = default) => cacheUserRoles.RemoveAsync(r => ids.Contains(r.Id), token); public bool HasPermission(IEnumerable rolesIds, string permissionName) { var permissionInfo = cachePermission.FirstOrDefault(p => p.Name.ToLower() == permissionName.ToLower()); if (permissionInfo is null) return false; var idPermissionInfo = permissionInfo.Id; var roles = cacheUserRoles.Where(r => rolesIds.Contains(r.Id)); foreach (var role in roles) if (HasPermission(role, idPermissionInfo)) return true; return false; } private bool HasPermission(UserRole userRole, int idPermission, int recursionLevel = 7) { if (userRole.RelationUserRolePermissions.Any(p => p.IdPermission == idPermission)) return true; if (userRole.IdParent is not null && recursionLevel > 0) { var parentRole = cacheUserRoles.FirstOrDefault(p => p.Id == userRole.IdParent); return HasPermission(parentRole, idPermission, --recursionLevel); } return false; } } }