forked from ddrilling/AsbCloudServer
299 lines
9.7 KiB
C#
299 lines
9.7 KiB
C#
using AsbCloudApp.Comparators;
|
|
using AsbCloudApp.Data;
|
|
using AsbCloudApp.Data.User;
|
|
using AsbCloudApp.Exceptions;
|
|
using AsbCloudApp.Repositories;
|
|
using AsbCloudDb;
|
|
using AsbCloudDb.Model;
|
|
using Mapster;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Caching.Memory;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace AsbCloudInfrastructure.Repository;
|
|
|
|
|
|
public class UserRoleRepository : IUserRoleRepository
|
|
{
|
|
private readonly IAsbCloudDbContext dbContext;
|
|
private readonly IMemoryCache memoryCache;
|
|
|
|
public UserRoleRepository(IAsbCloudDbContext dbContext, IMemoryCache memoryCache)
|
|
{
|
|
this.dbContext = dbContext;
|
|
this.memoryCache = memoryCache;
|
|
}
|
|
|
|
public async Task<int> InsertAsync(UserRoleDto dto, CancellationToken token)
|
|
{
|
|
var entity = dto.Adapt<UserRole>();
|
|
var updatedEntity = dbContext.UserRoles.Add(entity);
|
|
await dbContext.SaveChangesAsync(token);
|
|
|
|
if (updatedEntity.IsKeySet)
|
|
{
|
|
dto.Id = updatedEntity.Entity.Id;
|
|
await UpdatePermissionsAsync(dto, token);
|
|
await UpdateIncludedRolesAsync(dto, token);
|
|
|
|
DropCacheUserRole();
|
|
return updatedEntity.Entity.Id;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
public Task<int> InsertRangeAsync(IEnumerable<UserRoleDto> newItems, CancellationToken token)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public async Task<IEnumerable<UserRoleDto>> GetAllAsync(CancellationToken token)
|
|
{
|
|
var entities = await GetCacheUserRoleAsync(token)
|
|
.ConfigureAwait(false);
|
|
|
|
return entities.Select(Convert);
|
|
}
|
|
|
|
public UserRoleDto? GetOrDefault(int id)
|
|
{
|
|
var entity = GetCacheUserRole().FirstOrDefault(x => x.Id == id);
|
|
if (entity is null)
|
|
return null;
|
|
return Convert(entity);
|
|
}
|
|
|
|
public async Task<UserRoleDto?> GetOrDefaultAsync(int id, CancellationToken token)
|
|
{
|
|
var entity = (await GetCacheUserRoleAsync(token)
|
|
.ConfigureAwait(false)).FirstOrDefault(r => r.Id == id);
|
|
if (entity is null)
|
|
return null;
|
|
return Convert(entity);
|
|
}
|
|
|
|
public async Task<IEnumerable<UserRoleDto>> GetByNamesAsync(IEnumerable<string> names, CancellationToken token)
|
|
{
|
|
if (names?.Any() != true)
|
|
return Enumerable.Empty<UserRoleDto>();
|
|
|
|
var entities = (await GetCacheUserRoleAsync(token))
|
|
.Where(r => names.Contains(r.Caption));
|
|
|
|
if (entities?.Count() != names.Count())
|
|
throw new ArgumentInvalidException(nameof(names), "Invalid role names");
|
|
|
|
return entities.Select(Convert);
|
|
}
|
|
|
|
public async Task<int> UpdateAsync(UserRoleDto dto, CancellationToken token)
|
|
{
|
|
await UpdatePermissionsAsync(dto, token);
|
|
await UpdateIncludedRolesAsync(dto, token);
|
|
|
|
var entity = Convert(dto);
|
|
var result = dbContext.UserRoles.Upsert(entity);
|
|
await dbContext.SaveChangesAsync(token);
|
|
DropCacheUserRole();
|
|
return result.Entity.Id;
|
|
}
|
|
|
|
public IEnumerable<UserRoleDto> GetNestedById(int id, int recursionLevel = 7)
|
|
{
|
|
var role = GetCacheUserRole()
|
|
.FirstOrDefault(r => r.Id == id);
|
|
if (role is null)
|
|
return Enumerable.Empty<UserRoleDto>();
|
|
|
|
var roles = new SortedSet<UserRoleDto>(ComparerIId.GetInstance()) { Convert(role) };
|
|
|
|
if (recursionLevel <= 0 || role.RelationUserRoleUserRoles?.Any() != true)
|
|
return roles;
|
|
|
|
foreach (var relation in role.RelationUserRoleUserRoles)
|
|
{
|
|
var nestedRoles = GetNestedById(relation.IdInclude, --recursionLevel);
|
|
if (nestedRoles?.Any() != true)
|
|
continue;
|
|
foreach (var nestedRole in nestedRoles)
|
|
roles.Add(nestedRole);
|
|
}
|
|
return roles;
|
|
}
|
|
|
|
public async Task<int> DeleteAsync(int id, CancellationToken token)
|
|
{
|
|
var entity = (await GetCacheUserRoleAsync(token)).FirstOrDefault(r => r.Id == id);
|
|
|
|
if (entity is not null)
|
|
{
|
|
var removeEntity = dbContext.UserRoles.Remove(entity);
|
|
await dbContext.SaveChangesAsync(token);
|
|
DropCacheUserRole();
|
|
return removeEntity.Entity.Id;
|
|
}
|
|
else return 0;
|
|
}
|
|
|
|
public bool HasPermission(IEnumerable<int> rolesIds, string permissionName)
|
|
{
|
|
var permissionInfo = GetCacheRelationUserRolePermissions()
|
|
.FirstOrDefault(p => p. Permission?.Name.ToLower() == permissionName.ToLower())
|
|
?.Permission;
|
|
|
|
if (permissionInfo is null)
|
|
return false;
|
|
|
|
if (rolesIds.Contains(1))
|
|
return true;
|
|
|
|
var idPermissionInfo = permissionInfo.Id;
|
|
var entities = GetCacheUserRole()
|
|
.Where(r => rolesIds.Contains(r.Id));
|
|
|
|
foreach (var role in entities)
|
|
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 (recursionLevel <= 0 || userRole.RelationUserRoleUserRoles?.Any() != true)
|
|
return false;
|
|
|
|
foreach (var relation in userRole.RelationUserRoleUserRoles)
|
|
{
|
|
var entity = GetCacheUserRole()
|
|
.First(p => p.Id == relation.IdInclude);
|
|
if (HasPermission(entity, idPermission, --recursionLevel))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private IEnumerable<UserRoleDto> GetNestedByIds(IEnumerable<int> ids, int recursionLevel = 7)
|
|
{
|
|
var roles = new List<UserRoleDto>();
|
|
foreach (var id in ids)
|
|
roles.AddRange(GetNestedById(id, recursionLevel));
|
|
|
|
return roles;
|
|
}
|
|
|
|
private async Task UpdateIncludedRolesAsync(UserRoleDto dto, CancellationToken token)
|
|
{
|
|
if (!dto.Roles.Any())
|
|
return;
|
|
|
|
var idsIncludeRole = GetNestedByIds(dto.Roles.Select(x => x.Id)).Select(x => x.Id);
|
|
|
|
if (idsIncludeRole.Any(x => x == dto.Id))
|
|
throw new ArgumentInvalidException(nameof(dto), "Invalid include role (self reference)");
|
|
|
|
var removeRelationsQuery = dbContext.RelationUserRoleUserRoles
|
|
.Where(r => r.Id == dto.Id);
|
|
|
|
dbContext.RelationUserRoleUserRoles.RemoveRange(removeRelationsQuery);
|
|
var newRelations = dto.Roles.Select(r => new RelationUserRoleUserRole
|
|
{
|
|
Id = dto.Id,
|
|
IdInclude = r.Id,
|
|
});
|
|
dbContext.RelationUserRoleUserRoles.AddRange(newRelations);
|
|
await dbContext.SaveChangesAsync(token);
|
|
DropCacheRelationUserRoleUserRole();
|
|
}
|
|
|
|
private async Task UpdatePermissionsAsync(UserRoleDto dto, CancellationToken token)
|
|
{
|
|
if (dto?.Permissions is null)
|
|
return;
|
|
|
|
var relations = await dbContext.RelationUserRolePermissions
|
|
.Where(r => r.IdUserRole == dto.Id)
|
|
.ToListAsync(token)
|
|
.ConfigureAwait(false);
|
|
dbContext.RelationUserRolePermissions.RemoveRange(relations);
|
|
|
|
if (dto.Permissions.Any())
|
|
{
|
|
var newRelations = dto.Permissions.Select(p => new RelationUserRolePermission
|
|
{
|
|
IdPermission = p.Id,
|
|
IdUserRole = dto.Id,
|
|
});
|
|
|
|
await dbContext.RelationUserRolePermissions.AddRangeAsync(newRelations, token);
|
|
await dbContext.SaveChangesAsync(token);
|
|
}
|
|
DropCacheRelationUserRolePermissions();
|
|
}
|
|
|
|
private Task<IEnumerable<UserRole>> GetCacheUserRoleAsync(CancellationToken token)
|
|
=> memoryCache.GetOrCreateBasicAsync(dbContext.Set<UserRole>()
|
|
.Include(r => r.RelationUserRolePermissions)
|
|
.Include(r => r.RelationUserRoleUserRoles)
|
|
.Include(r => r.RelationUsersUserRoles), token);
|
|
|
|
private IEnumerable<UserRole> GetCacheUserRole()
|
|
=> memoryCache.GetOrCreateBasic(dbContext.Set<UserRole>()
|
|
.Include(r => r.RelationUserRolePermissions)
|
|
.Include(r => r.RelationUserRoleUserRoles)
|
|
.Include(r => r.RelationUsersUserRoles));
|
|
|
|
private void DropCacheUserRole()
|
|
=> memoryCache.DropBasic<UserRole>();
|
|
|
|
private void DropCacheRelationUserRoleUserRole()
|
|
=> memoryCache.DropBasic<RelationUserUserRole>();
|
|
|
|
private IEnumerable<RelationUserRolePermission> GetCacheRelationUserRolePermissions()
|
|
=> memoryCache.GetOrCreateBasic(dbContext.Set<RelationUserRolePermission>()
|
|
.Include(r => r.UserRole)
|
|
.Include(r => r.Permission));
|
|
|
|
private void DropCacheRelationUserRolePermissions()
|
|
=> memoryCache.DropBasic<RelationUserRolePermission>();
|
|
|
|
private UserRoleDto Convert(UserRole entity)
|
|
{
|
|
var dto = entity.Adapt<UserRoleDto>();
|
|
if (entity.RelationUserRolePermissions?.Any() == true)
|
|
{
|
|
dto.Permissions = GetCacheRelationUserRolePermissions()
|
|
.Where(r => entity.Id == r.IdUserRole)
|
|
.Select(r => Convert(r.Permission));
|
|
}
|
|
|
|
if (entity.RelationUserRoleUserRoles?.Any() == true)
|
|
{
|
|
var rolesCache = GetCacheUserRole();
|
|
dto.Roles = entity.RelationUserRoleUserRoles
|
|
.Select(rel => Convert(rolesCache
|
|
.First(r => r.Id == rel.IdInclude)))
|
|
.ToArray();
|
|
}
|
|
return dto;
|
|
}
|
|
|
|
private static PermissionDto Convert(Permission entity)
|
|
{
|
|
var dto = entity.Adapt<PermissionDto>();
|
|
return dto;
|
|
}
|
|
|
|
private static UserRole Convert(UserRoleDto dto)
|
|
{
|
|
var entity = dto.Adapt<UserRole>();
|
|
return entity;
|
|
}
|
|
}
|