refactor UserRoleService

This commit is contained in:
Фролов 2021-12-20 15:17:09 +05:00
parent e99f0ff9c9
commit a0208f412e
14 changed files with 88 additions and 272 deletions

View File

@ -1,9 +0,0 @@
namespace AsbCloudApp.Data
{
public class PermissionBaseDto
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
}

View File

@ -1,7 +1,9 @@
namespace AsbCloudApp.Data namespace AsbCloudApp.Data
{ {
public class PermissionDto : PermissionBaseDto public class PermissionDto: IId
{ {
public int IdUserRole { get; set; } public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
} }
} }

View File

@ -9,7 +9,7 @@ namespace AsbCloudApp.Data
public string Caption { get; set; } public string Caption { get; set; }
public int? IdParent { get; set; } public int? IdParent { get; set; }
public int IdType { get; set; } public int IdType { get; set; }
public IEnumerable<PermissionBaseDto> Permissions { get; set; } public IEnumerable<PermissionDto> Permissions { get; set; }
[JsonIgnore] [JsonIgnore]
public virtual ICollection<UserDto> Users { get; set; } public virtual ICollection<UserDto> Users { get; set; }
} }

View File

@ -4,7 +4,7 @@ namespace AsbCloudApp.Data
{ {
public class UserTokenDto : UserExtendedDto public class UserTokenDto : UserExtendedDto
{ {
public IEnumerable<PermissionBaseDto> Permissions { get; set; } public IEnumerable<PermissionDto> Permissions { get; set; }
public string Token { get; set; } public string Token { get; set; }
} }
} }

View File

@ -1,16 +0,0 @@
using AsbCloudApp.Data;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudApp.Services
{
public interface IPermissionService
{
Task<IEnumerable<PermissionDto>> GetByIdRoleAsync(int idRole, CancellationToken token);
Task<int> InsertRangeAsync(IEnumerable<PermissionDto> dtos, CancellationToken token);
Task<int> UpdateAsync(PermissionDto dto, CancellationToken token);
Task<int> DeleteAsync(int idUserRole, int idPermission, CancellationToken token);
Task<int> DeleteAllByRoleAsync(int idUserRole, CancellationToken token);
}
}

View File

@ -9,7 +9,6 @@ namespace AsbCloudApp.Services
{ {
Task<UserRoleDto> GetByNameAsync(string name, CancellationToken token = default); Task<UserRoleDto> GetByNameAsync(string name, CancellationToken token = default);
List<UserRoleDto> GetNestedById(int id, int counter = 10); List<UserRoleDto> GetNestedById(int id, int counter = 10);
IEnumerable<PermissionBaseDto> GetNestedPermissions(IEnumerable<UserRoleDto> roles);
bool HasPermission(IEnumerable<int> rolesIds, string permissionName); bool HasPermission(IEnumerable<int> rolesIds, string permissionName);
} }
} }

View File

@ -6,7 +6,7 @@ namespace AsbCloudApp.Services
public interface IUserService : ICrudService<UserExtendedDto> public interface IUserService : ICrudService<UserExtendedDto>
{ {
IUserRoleService RoleService { get; } IUserRoleService RoleService { get; }
IEnumerable<PermissionBaseDto> GetNestedPermissions(int idUser); IEnumerable<PermissionDto> GetNestedPermissions(int idUser);
IEnumerable<UserRoleDto> GetRolesByIdUser(int idUser); IEnumerable<UserRoleDto> GetRolesByIdUser(int idUser);
bool HasAnyRoleOf(int idUser, IEnumerable<string> roleNames); bool HasAnyRoleOf(int idUser, IEnumerable<string> roleNames);
bool HasAnyRoleOf(int idUser, IEnumerable<int> roleIds); bool HasAnyRoleOf(int idUser, IEnumerable<int> roleIds);

View File

@ -49,7 +49,6 @@ namespace AsbCloudInfrastructure
services.AddTransient<IMeasureService, MeasureService>(); services.AddTransient<IMeasureService, MeasureService>();
services.AddTransient<IMessageService, MessageService>(); services.AddTransient<IMessageService, MessageService>();
services.AddTransient<IOperationsStatService, OperationsStatService>(); services.AddTransient<IOperationsStatService, OperationsStatService>();
services.AddTransient<IPermissionService, PermissionService>();
services.AddTransient<IReportService, ReportService>(); services.AddTransient<IReportService, ReportService>();
services.AddTransient<ISetpointsService, SetpointsService>(); services.AddTransient<ISetpointsService, SetpointsService>();
services.AddTransient<ITelemetryAnalyticsService, TelemetryAnalyticsService>(); services.AddTransient<ITelemetryAnalyticsService, TelemetryAnalyticsService>();
@ -70,6 +69,7 @@ namespace AsbCloudInfrastructure
services.AddTransient<ICrudService<DepositDto>, CrudServiceBase<DepositDto, Deposit>>(); services.AddTransient<ICrudService<DepositDto>, CrudServiceBase<DepositDto, Deposit>>();
services.AddTransient<ICrudService<CompanyDto>, CrudServiceBase<CompanyDto, Company>>(); services.AddTransient<ICrudService<CompanyDto>, CrudServiceBase<CompanyDto, Company>>();
services.AddTransient<ICrudService<ClusterDto>, CrudServiceBase<ClusterDto, Cluster>>(); services.AddTransient<ICrudService<ClusterDto>, CrudServiceBase<ClusterDto, Cluster>>();
services.AddTransient<ICrudService<PermissionDto>, CrudCacheServiceBase<PermissionDto, Permission>>();
// TelemetryData services // TelemetryData services
services.AddTransient<ITelemetryDataService<TelemetryDataSaubDto>, TelemetryDataSaubService>(); services.AddTransient<ITelemetryDataService<TelemetryDataSaubDto>, TelemetryDataSaubService>();

View File

@ -35,7 +35,7 @@ namespace AsbCloudInfrastructure.Services
private readonly HashAlgorithm hashAlgorithm; private readonly HashAlgorithm hashAlgorithm;
private readonly Random rnd; private readonly Random rnd;
public AuthService(IAsbCloudDbContext db, CacheDb cacheDb, IUserService userService) public AuthService(IAsbCloudDbContext db, IUserService userService)
{ {
this.db = db; this.db = db;
this.userService = userService; this.userService = userService;

View File

@ -1,90 +0,0 @@
using AsbCloudApp.Data;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.Services.Cache;
using Mapster;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Services
{
public class PermissionService : IPermissionService
{
private readonly CacheTable<RelationUserRolePermission> cacheUserRolePermission;
private readonly CacheTable<Permission> cachePermission;
public PermissionService(IAsbCloudDbContext db, CacheDb cacheDb)
{
cacheUserRolePermission = cacheDb.GetCachedTable<RelationUserRolePermission>(
(AsbCloudDbContext)db,
new string[] { nameof(RelationUserRolePermission.Permission) });
cachePermission = cacheDb.GetCachedTable<Permission>(
(AsbCloudDbContext)db);
}
public async Task<IEnumerable<PermissionDto>> GetByIdRoleAsync(int idRole, CancellationToken token)
{
var entities = await cacheUserRolePermission
.WhereAsync(p => p.IdUserRole == idRole, token)
.ConfigureAwait(false);
var dto = entities.Select(e => e.Permission).Adapt<PermissionDto>();
return dto;
}
public async Task<int> InsertRangeAsync(IEnumerable<PermissionDto> dtos, CancellationToken token)
{
foreach (var dto in dtos)
{
var entity = Convert(dto);
var insertedEntity = cachePermission.Insert(entity);
cacheUserRolePermission.Insert(new RelationUserRolePermission()
{
IdPermission = insertedEntity.Id,
IdUserRole = dto.IdUserRole
});
}
return 1;
}
public async Task<int> UpdateAsync(PermissionDto dto, CancellationToken token)
{
var entity = Convert(dto);
await cachePermission.UpsertAsync(entity, token)
.ConfigureAwait(false);
return 1;
}
public async Task<int> DeleteAsync(int idUserRole, int idPermission, CancellationToken token)
{
bool predicate(RelationUserRolePermission p) => p.IdUserRole == idUserRole && p.IdPermission == idPermission;
await DeleteAsync(predicate, token);
cachePermission.Remove(p => p.Id == idPermission);
return 1;
}
public Task<int> DeleteAllByRoleAsync(int idUserRole, CancellationToken token)
{
bool predicate(RelationUserRolePermission p) => p.IdUserRole == idUserRole;
return DeleteAsync(predicate, token);
}
private async Task<int> DeleteAsync(Func<RelationUserRolePermission, bool> predicate, CancellationToken token)
{
var count = (await cacheUserRolePermission.WhereAsync(predicate, token).ConfigureAwait(false)).Count();
if (count > 0)
await cacheUserRolePermission.RemoveAsync(predicate, token)
.ConfigureAwait(false);
return count;
}
public Permission Convert(PermissionDto src)
{
var entity = src.Adapt<Permission>();
return entity;
}
}
}

View File

@ -1,4 +1,3 @@
using System.Diagnostics;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
@ -15,15 +14,14 @@ namespace AsbCloudInfrastructure.Services
public class UserRoleService : IUserRoleService public class UserRoleService : IUserRoleService
{ {
private readonly CacheTable<UserRole> cacheUserRoles; private readonly CacheTable<UserRole> cacheUserRoles;
private readonly CacheTable<Permission> cachePermission; private readonly CacheTable<RelationUserRolePermission> cacheUserRolePermissions;
private readonly IPermissionService permissionService;
public List<string> Includes { get; } = new();
public UserRoleService(IAsbCloudDbContext context, CacheDb cacheDb, IPermissionService permissionService) public List<string> Includes { get; }
public UserRoleService(IAsbCloudDbContext context, CacheDb cacheDb)
{ {
cacheUserRoles = cacheDb.GetCachedTable<UserRole>((AsbCloudDbContext)context, new [] { nameof(UserRole.RelationUserRolePermissions) }); cacheUserRoles = cacheDb.GetCachedTable<UserRole>((AsbCloudDbContext)context, new [] { nameof(UserRole.RelationUserRolePermissions) });
cachePermission = cacheDb.GetCachedTable<Permission>((AsbCloudDbContext)context); cacheUserRolePermissions = cacheDb.GetCachedTable<RelationUserRolePermission>((AsbCloudDbContext)context, new[] { nameof(RelationUserRolePermission.Permission) });
this.permissionService = permissionService;
} }
public async Task<int> InsertAsync(UserRoleDto dto, CancellationToken token = default) public async Task<int> InsertAsync(UserRoleDto dto, CancellationToken token = default)
@ -31,6 +29,7 @@ namespace AsbCloudInfrastructure.Services
var entity = dto.Adapt<UserRole>(); var entity = dto.Adapt<UserRole>();
var updatedEntity = await cacheUserRoles.InsertAsync(entity, token) var updatedEntity = await cacheUserRoles.InsertAsync(entity, token)
.ConfigureAwait(false); .ConfigureAwait(false);
dto.Id = updatedEntity.Id;
await UpdatePermissionsAsync(dto, token); await UpdatePermissionsAsync(dto, token);
await cacheUserRoles.RefreshAsync(true, token) await cacheUserRoles.RefreshAsync(true, token)
.ConfigureAwait(false); .ConfigureAwait(false);
@ -48,7 +47,7 @@ namespace AsbCloudInfrastructure.Services
{ {
var entities = await cacheUserRoles.WhereAsync(token) var entities = await cacheUserRoles.WhereAsync(token)
.ConfigureAwait(false); .ConfigureAwait(false);
var dtos = entities.Adapt<UserRoleDto>(); var dtos = entities?.Select(Convert);
return dtos; return dtos;
} }
@ -56,7 +55,9 @@ namespace AsbCloudInfrastructure.Services
{ {
var entity = await cacheUserRoles.FirstOrDefaultAsync(r=>r.Id == id, token) var entity = await cacheUserRoles.FirstOrDefaultAsync(r=>r.Id == id, token)
.ConfigureAwait(false); .ConfigureAwait(false);
var dto = entity?.Adapt<UserRoleDto>(); if (entity is null)
return null;
var dto = Convert(entity);
return dto; return dto;
} }
@ -64,20 +65,33 @@ namespace AsbCloudInfrastructure.Services
{ {
var entity = await cacheUserRoles.FirstOrDefaultAsync(r => r.Caption == name, token) var entity = await cacheUserRoles.FirstOrDefaultAsync(r => r.Caption == name, token)
.ConfigureAwait(false); .ConfigureAwait(false);
var dto = entity?.Adapt<UserRoleDto>(); if (entity is null)
return null;
var dto = Convert(entity);
return dto; return dto;
} }
public async Task<int> UpdateAsync(int id, UserRoleDto dto, CancellationToken token = default) public async Task<int> UpdateAsync(int id, UserRoleDto dto, CancellationToken token = default)
{ {
dto.Id = id; if (dto.Id != id)
var entity = dto.Adapt<UserRole>(); {
var exist = await cacheUserRoles.ContainsAsync(i => i.Id == dto.Id, token)
.ConfigureAwait(false);
if (exist)
return -1;
await cacheUserRoles.RemoveAsync(i => i.Id == id, token)
.ConfigureAwait(false);
}
var entity = Convert(dto);
await UpdatePermissionsAsync(dto, token); await UpdatePermissionsAsync(dto, token);
await cacheUserRoles.UpsertAsync(entity, token) await cacheUserRoles.UpsertAsync(entity, token)
.ConfigureAwait(false); .ConfigureAwait(false);
return id; return dto.Id;
} }
public List<UserRoleDto> GetNestedById(int id, int recursionLevel = 7) public List<UserRoleDto> GetNestedById(int id, int recursionLevel = 7)
@ -85,7 +99,7 @@ namespace AsbCloudInfrastructure.Services
var role = cacheUserRoles.FirstOrDefault(r => r.Id == id); var role = cacheUserRoles.FirstOrDefault(r => r.Id == id);
if (role is null) if (role is null)
return null; return null;
var dto = role.Adapt<UserRoleDto>(); var dto = Convert(role);
if (role.IdParent is null || recursionLevel == 0) if (role.IdParent is null || recursionLevel == 0)
return new List<UserRoleDto> { dto }; return new List<UserRoleDto> { dto };
var parentRoles = GetNestedById((int)role.IdParent, --recursionLevel) ?? var parentRoles = GetNestedById((int)role.IdParent, --recursionLevel) ??
@ -94,76 +108,28 @@ namespace AsbCloudInfrastructure.Services
return parentRoles; return parentRoles;
} }
public IEnumerable<PermissionBaseDto> GetNestedPermissions(IEnumerable<UserRoleDto> roles)
{
var permissions = new Dictionary<int, PermissionBaseDto>(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<PermissionBaseDto>();
}
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) private async Task UpdatePermissionsAsync(UserRoleDto roleDto, CancellationToken token)
{ {
await permissionService.DeleteAllByRoleAsync(roleDto.Id, token) if (roleDto?.Permissions is null)
return;
await cacheUserRolePermissions.RemoveAsync(r => r.IdUserRole == roleDto.Id, token)
.ConfigureAwait(false); .ConfigureAwait(false);
if (!roleDto.Permissions.Any()) if (!roleDto.Permissions.Any())
return; return;
var newPermissions = roleDto.Permissions.Select(p => new PermissionDto var newRelationRoleToPermission = roleDto.Permissions.Select(p => new RelationUserRolePermission
{ {
Id = p.Id, IdPermission = p.Id,
IdUserRole = roleDto.Id, IdUserRole = roleDto.Id,
Name = p.Name
}); });
await permissionService.InsertRangeAsync(newPermissions, token) await cacheUserRolePermissions.InsertAsync(newRelationRoleToPermission, token)
.ConfigureAwait(false); .ConfigureAwait(false);
} }
private IEnumerable<Permission> 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<int> DeleteAsync(int id, CancellationToken token = default) public Task<int> DeleteAsync(int id, CancellationToken token = default)
=> cacheUserRoles.RemoveAsync(r => r.Id == id, token); => cacheUserRoles.RemoveAsync(r => r.Id == id, token);
@ -173,7 +139,9 @@ namespace AsbCloudInfrastructure.Services
public bool HasPermission(IEnumerable<int> rolesIds, string permissionName) public bool HasPermission(IEnumerable<int> rolesIds, string permissionName)
{ {
var permissionInfo = cachePermission.FirstOrDefault(p => p.Name.ToLower() == permissionName.ToLower()); var permissionInfo = cacheUserRolePermissions
.FirstOrDefault(p => p.Permission?.Name.ToLower() == permissionName.ToLower())
?.Permission;
if (permissionInfo is null) if (permissionInfo is null)
return false; return false;
@ -197,5 +165,30 @@ namespace AsbCloudInfrastructure.Services
} }
return false; return false;
} }
private static UserRole Convert(UserRoleDto dto)
{
var entity = dto.Adapt<UserRole>();
return entity;
}
private UserRoleDto Convert(UserRole entity)
{
var dto = entity.Adapt<UserRoleDto>();
if(entity.RelationUserRolePermissions?.Any() == true)
{
var permissionsIds = entity.RelationUserRolePermissions.Select(r => r.IdPermission);
dto.Permissions = cacheUserRolePermissions
.Where(r => permissionsIds.Contains(r.IdPermission))
.Select(r => Convert(r.Permission));
}
return dto;
}
private static PermissionDto Convert(Permission entity)
{
var dto = entity.Adapt<PermissionDto>();
return dto;
}
} }
} }

View File

@ -101,10 +101,10 @@ namespace AsbCloudInfrastructure.Services
return roles.SelectMany(r => RoleService.GetNestedById(r.IdUserRole)); return roles.SelectMany(r => RoleService.GetNestedById(r.IdUserRole));
} }
public IEnumerable<PermissionBaseDto> GetNestedPermissions(int idUser) public IEnumerable<PermissionDto> GetNestedPermissions(int idUser)
{ {
var roles = GetRolesByIdUser(idUser); var roles = GetRolesByIdUser(idUser);
return RoleService.GetNestedPermissions(roles); return roles.SelectMany(r => r.Permissions);
} }
private async Task UpdateRolesCacheForUserAsync(int idUser, IEnumerable<string> newRoleNames, CancellationToken token) private async Task UpdateRolesCacheForUserAsync(int idUser, IEnumerable<string> newRoleNames, CancellationToken token)

View File

@ -11,74 +11,10 @@ namespace AsbCloudWebApi.Controllers
[Route("api/admin/permission")] [Route("api/admin/permission")]
[ApiController] [ApiController]
[Authorize] [Authorize]
public class AdminPermissionController : ControllerBase public class AdminPermissionController : CrudController<PermissionDto, ICrudService<PermissionDto>>
{ {
private readonly IPermissionService permissionService; public AdminPermissionController(ICrudService<PermissionDto> permissionService)
:base(permissionService)
public AdminPermissionController(IPermissionService permissionService) {}
{
this.permissionService = permissionService;
}
/// <summary>
/// Получает список всех разрешений для роли
/// </summary>
/// <param name="idRole"> id роли </param>
/// <param name="token"> Токен отмены задачи </param>
/// <returns></returns>
[HttpGet]
[ProducesResponseType(typeof(IEnumerable<PermissionDto>), (int)System.Net.HttpStatusCode.OK)]
[Permission]
public async Task<IActionResult> GetByIdRoleAsync(int idRole, CancellationToken token = default)
{
var result = await permissionService.GetByIdRoleAsync(idRole, token);
return Ok(result);
}
/// <summary>
/// Добавляет разрешения для роли
/// </summary>
/// <param name="dtos"> Объекты новых разрешений для справочника </param>
/// <param name="token"> Токен отмены задачи </param>
/// <returns></returns>
[HttpPost]
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> InsertRangeAsync(IEnumerable<PermissionDto> dtos,
CancellationToken token = default)
{
var result =await permissionService.InsertRangeAsync(dtos, token);
return Ok(result);
}
/// <summary>
/// Обновляет разрешение для роли
/// </summary>
/// <param name="dto"> Объект разрешения </param>
/// <param name="token"> Токен отмены задачи </param>
/// <returns></returns>
[HttpPut]
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> UpdateAsync(PermissionDto dto,
CancellationToken token = default)
{
var result = await permissionService.UpdateAsync(dto, token);
return Ok(result);
}
/// <summary>
/// Удаляет разрешение для роли
/// </summary>
/// <param name="idPermission"> id разрешения </param>
/// <param name="idUserRole"> id роли для удаления разрешения </param>
/// <param name="token"> Токен отмены задачи </param>
/// <returns></returns>
[HttpDelete("{idPermission}/{idUserRole}")]
[ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> DeleteAsync(int idUserRole, int idPermission,
CancellationToken token = default)
{
var result = await permissionService.DeleteAsync(idUserRole, idPermission, token);
return Ok(result);
}
} }
} }

View File

@ -2,6 +2,8 @@
using AsbCloudApp.Services; using AsbCloudApp.Services;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudWebApi.Controllers namespace AsbCloudWebApi.Controllers
{ {
@ -14,6 +16,5 @@ namespace AsbCloudWebApi.Controllers
:base(service) :base(service)
{} {}
} }
} }