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 cachePermissionInfo; 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.Permissions) }); cachePermissionInfo = 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.IdPermissionInfo)) { permissions[newPermission.IdPermissionInfo].Value |= newPermission.Value; } else { permissions.Add(newPermission.IdPermissionInfo, new PermissionBaseDto { IdPermissionInfo = newPermission.IdPermissionInfo, PermissionName = newPermission.PermissionInfo?.Name ?? cachePermissionInfo.FirstOrDefault(p => p.Id == newPermission.IdPermissionInfo).Name, Value = newPermission.Value, }); } } } 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 { IdPermissionInfo = p.IdPermissionInfo, IdUserRole = roleDto.Id, PermissionName = p.PermissionName, Value = p.Value, }); await permissionService.InsertRangeAsync(newPermissions, token) .ConfigureAwait(false); } private IEnumerable GetNestedPermissions(UserRole role, int recursionLevel = 7) { var permissions = role.Permissions.ToList(); if (role.IdParent is null) return permissions; if (recursionLevel == 0) { Trace.WriteLine($"User role with id: {role.Id} has more than 10 nested childs"); return permissions; } var parentRole = cacheUserRoles.FirstOrDefault(r => r.Id == role.IdParent); if (parentRole is null) return permissions; var parentPermissions = GetNestedPermissions(parentRole, --recursionLevel); return permissions.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, int permissionMask = 0) { var permissionInfo = cachePermissionInfo.FirstOrDefault(p => p.Name.ToLower() == permissionName.ToLower()); if (permissionInfo is null) return false; if (permissionMask == 0) permissionMask = -1; var idPermissionInfo = permissionInfo.Id; var roles = cacheUserRoles.Where(r => rolesIds.Contains(r.Id)); foreach (var role in roles) if (HasPermission(role, idPermissionInfo, permissionMask)) return true; return false; } private bool HasPermission(UserRole userRole, int idPermissionInfo, int permissionMask, int recursionLevel = 7) { if (userRole.Permissions.Any(p => p.IdPermissionInfo == idPermissionInfo && (p.Value & permissionMask) > 0)) return true; if (userRole.IdParent is not null && recursionLevel > 0) { var parentRole = cacheUserRoles.FirstOrDefault(p => p.Id == userRole.IdParent); return HasPermission(parentRole, idPermissionInfo, permissionMask, --recursionLevel); } return false; } } }