forked from ddrilling/AsbCloudServer
Add/refactor services for permissions authorization model.
Rename some fields in DB.permission.
This commit is contained in:
parent
1ec22744c3
commit
551c60c4ff
@ -1,9 +1,7 @@
|
||||
namespace AsbCloudApp.Data
|
||||
{
|
||||
public class PermissionDto
|
||||
public class PermissionDto : PermissionBaseDto
|
||||
{
|
||||
public int IdUserRole { get; set; }
|
||||
public int IdPermission { get; set; }
|
||||
public int PermissionValue { get; set; }
|
||||
}
|
||||
}
|
9
AsbCloudApp/Data/PermissionRoleDto.cs
Normal file
9
AsbCloudApp/Data/PermissionRoleDto.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace AsbCloudApp.Data
|
||||
{
|
||||
public class PermissionBaseDto
|
||||
{
|
||||
public int IdPermissionInfo { get; set; }
|
||||
public string PermissionName { get; set; }
|
||||
public int Value { get; set; }
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
namespace AsbCloudApp.Data
|
||||
{
|
||||
public class UserBaseDto
|
||||
{
|
||||
public string Login { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Surname { get; set; }
|
||||
|
||||
public string Patronymic { get; set; }
|
||||
|
||||
public string Email { get; set; }
|
||||
|
||||
public string Phone { get; set; }
|
||||
|
||||
public string Position { get; set; }
|
||||
}
|
||||
}
|
@ -1,13 +1,16 @@
|
||||
namespace AsbCloudApp.Data
|
||||
{
|
||||
public class UserDto : UserBaseDto, IId
|
||||
public class UserDto: IId
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public string Login { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Surname { get; set; }
|
||||
public string Patronymic { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string Phone { get; set; }
|
||||
public string Position { get; set; }
|
||||
public int? IdCompany { get; set; }
|
||||
|
||||
public string Password { get; set; }
|
||||
|
||||
public CompanyDto Company { get; set; }
|
||||
}
|
||||
}
|
||||
|
9
AsbCloudApp/Data/UserExtendedDto.cs
Normal file
9
AsbCloudApp/Data/UserExtendedDto.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AsbCloudApp.Data
|
||||
{
|
||||
public class UserExtendedDto : UserDto
|
||||
{
|
||||
public IEnumerable<string> RoleNames { get; set; }
|
||||
}
|
||||
}
|
9
AsbCloudApp/Data/UserRegistrationDto.cs
Normal file
9
AsbCloudApp/Data/UserRegistrationDto.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AsbCloudApp.Data
|
||||
{
|
||||
public class UserRegistrationDto : UserDto
|
||||
{
|
||||
public string Password { get; set; }
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@ namespace AsbCloudApp.Data
|
||||
public string Caption { get; set; }
|
||||
public int? IdParent { get; set; }
|
||||
public int IdType { get; set; }
|
||||
public IEnumerable<PermissionDto> Permissions { get; set; }
|
||||
public IEnumerable<PermissionBaseDto> Permissions { get; set; }
|
||||
[JsonIgnore]
|
||||
public virtual ICollection<UserDto> Users { get; set; }
|
||||
}
|
||||
|
@ -2,12 +2,9 @@
|
||||
|
||||
namespace AsbCloudApp.Data
|
||||
{
|
||||
public class UserTokenDto : UserBaseDto
|
||||
public class UserTokenDto : UserExtendedDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string CompanyName { get; set; }
|
||||
public IEnumerable<string> RoleNames { get; set; }
|
||||
public IDictionary<string, int> Permissions { get; set; }
|
||||
public IEnumerable<PermissionBaseDto> Permissions { get; set; }
|
||||
public string Token { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,6 @@ namespace AsbCloudApp.Services
|
||||
string password, CancellationToken token = default);
|
||||
|
||||
string Refresh(ClaimsPrincipal user);
|
||||
int Register(UserDto userDto);
|
||||
int Register(UserRegistrationDto userDto);
|
||||
}
|
||||
}
|
@ -1,8 +1,15 @@
|
||||
using AsbCloudApp.Data;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AsbCloudApp.Services
|
||||
{
|
||||
public interface IUserRoleService: ICrudService<UserRoleDto>
|
||||
public interface IUserRoleService : ICrudService<UserRoleDto>
|
||||
{
|
||||
Task<UserRoleDto> GetByNameAsync(string name, CancellationToken token = default);
|
||||
List<UserRoleDto> GetNestedById(int id, int counter = 10);
|
||||
IEnumerable<PermissionBaseDto> GetNestedPermissions(IEnumerable<UserRoleDto> roles);
|
||||
bool HasPermission(IEnumerable<int> rolesIds, string permissionName, int permissionMask = 0);
|
||||
}
|
||||
}
|
15
AsbCloudApp/Services/IUserService.cs
Normal file
15
AsbCloudApp/Services/IUserService.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using AsbCloudApp.Data;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AsbCloudApp.Services
|
||||
{
|
||||
public interface IUserService : ICrudService<UserExtendedDto>
|
||||
{
|
||||
IUserRoleService RoleService { get; }
|
||||
IEnumerable<PermissionBaseDto> GetNestedPermissions(int idUser);
|
||||
IEnumerable<UserRoleDto> GetRolesByIdUser(int idUser);
|
||||
bool HasAnyRoleOf(int idUser, IEnumerable<string> roleNames);
|
||||
bool HasAnyRoleOf(int idUser, IEnumerable<int> roleIds);
|
||||
public bool HasPermission(int idUser, string permissionName, int permissionMask = 0);
|
||||
}
|
||||
}
|
2978
AsbCloudDb/Migrations/20211210074746_Rename_Permissions_fields.Designer.cs
generated
Normal file
2978
AsbCloudDb/Migrations/20211210074746_Rename_Permissions_fields.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,67 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace AsbCloudDb.Migrations
|
||||
{
|
||||
public partial class Rename_Permissions_fields : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_t_permission_t_permission_info_id_permission",
|
||||
table: "t_permission");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "permission_value",
|
||||
table: "t_permission",
|
||||
newName: "value");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "id_permission",
|
||||
table: "t_permission",
|
||||
newName: "id_permission_info");
|
||||
|
||||
migrationBuilder.RenameIndex(
|
||||
name: "IX_t_permission_id_permission",
|
||||
table: "t_permission",
|
||||
newName: "IX_t_permission_id_permission_info");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_t_permission_t_permission_info_id_permission_info",
|
||||
table: "t_permission",
|
||||
column: "id_permission_info",
|
||||
principalTable: "t_permission_info",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_t_permission_t_permission_info_id_permission_info",
|
||||
table: "t_permission");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "value",
|
||||
table: "t_permission",
|
||||
newName: "permission_value");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "id_permission_info",
|
||||
table: "t_permission",
|
||||
newName: "id_permission");
|
||||
|
||||
migrationBuilder.RenameIndex(
|
||||
name: "IX_t_permission_id_permission_info",
|
||||
table: "t_permission",
|
||||
newName: "IX_t_permission_id_permission");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_t_permission_t_permission_info_id_permission",
|
||||
table: "t_permission",
|
||||
column: "id_permission",
|
||||
principalTable: "t_permission_info",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
}
|
||||
}
|
@ -687,17 +687,17 @@ namespace AsbCloudDb.Migrations
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id_user_role");
|
||||
|
||||
b.Property<int>("IdPermission")
|
||||
b.Property<int>("IdPermissionInfo")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id_permission");
|
||||
.HasColumnName("id_permission_info");
|
||||
|
||||
b.Property<int>("PermissionValue")
|
||||
b.Property<int>("Value")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("permission_value");
|
||||
.HasColumnName("value");
|
||||
|
||||
b.HasKey("IdUserRole", "IdPermission");
|
||||
b.HasKey("IdUserRole", "IdPermissionInfo");
|
||||
|
||||
b.HasIndex("IdPermission");
|
||||
b.HasIndex("IdPermissionInfo");
|
||||
|
||||
b.ToTable("t_permission");
|
||||
|
||||
@ -2605,7 +2605,7 @@ namespace AsbCloudDb.Migrations
|
||||
{
|
||||
b.HasOne("AsbCloudDb.Model.PermissionInfo", "PermissionInfo")
|
||||
.WithMany("Permissions")
|
||||
.HasForeignKey("IdPermission")
|
||||
.HasForeignKey("IdPermissionInfo")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
|
@ -256,7 +256,7 @@ namespace AsbCloudDb.Model
|
||||
|
||||
modelBuilder.Entity<Permission>(entity =>
|
||||
{
|
||||
entity.HasKey(e => new { e.IdUserRole, e.IdPermission });
|
||||
entity.HasKey(e => new { e.IdUserRole, e.IdPermissionInfo });
|
||||
});
|
||||
|
||||
FillData(modelBuilder);
|
||||
@ -456,13 +456,6 @@ namespace AsbCloudDb.Model
|
||||
select well;
|
||||
}
|
||||
|
||||
public IQueryable<User> GetUsersByLogin(string login)
|
||||
=> Users
|
||||
.Include(e => e.RelationUsersUserRoles)
|
||||
.ThenInclude(r => r.UserRole)
|
||||
.Include(e => e.Company)
|
||||
.Where(e => e.Login == login);
|
||||
|
||||
public async Task<(DateTime From, DateTime To)> GetDatesRangeAsync<TEntity>(int idTelemetry,
|
||||
CancellationToken token = default)
|
||||
where TEntity : class, ITelemetryData
|
||||
|
@ -51,7 +51,6 @@ namespace AsbCloudDb.Model
|
||||
DbSet<TEntity> Set<TEntity>() where TEntity : class;
|
||||
|
||||
IQueryable<Well> GetWellsForCompany(int idCompany);
|
||||
IQueryable<User> GetUsersByLogin(string login);
|
||||
Task<(DateTime From, DateTime To)> GetDatesRangeAsync<T>(int idTelemetry, CancellationToken token) where T : class, ITelemetryData;
|
||||
Task<IEnumerable<(double? MinDepth, double? MaxDepth, DateTime BeginPeriodDate)>> GetDepthToIntervalAsync(int telemetryId,
|
||||
int intervalHoursTimestamp, int workStartTimestamp, double timezoneOffset, CancellationToken token);
|
||||
|
@ -9,17 +9,17 @@ namespace AsbCloudDb.Model
|
||||
[Column("id_user_role")]
|
||||
public int IdUserRole { get; set; }
|
||||
|
||||
[Column("id_permission")]
|
||||
public int IdPermission { get; set; }
|
||||
[Column("id_permission_info")]
|
||||
public int IdPermissionInfo { get; set; }
|
||||
|
||||
[Column("permission_value")]
|
||||
public int PermissionValue { get; set; }
|
||||
[Column("value")]
|
||||
public int Value { get; set; }
|
||||
|
||||
[ForeignKey(nameof(IdUserRole))]
|
||||
[InverseProperty(nameof(Model.UserRole.Permissions))]
|
||||
public virtual UserRole UserRole { get; set; }
|
||||
|
||||
[ForeignKey(nameof(IdPermission))]
|
||||
[ForeignKey(nameof(IdPermissionInfo))]
|
||||
[InverseProperty(nameof(Model.PermissionInfo.Permissions))]
|
||||
public virtual PermissionInfo PermissionInfo { get; set; }
|
||||
}
|
||||
|
@ -1,4 +1,16 @@
|
||||
# Миграции
|
||||
## EF tools
|
||||
https://docs.microsoft.com/ru-ru/ef/core/cli/dotnet
|
||||
|
||||
Установка:
|
||||
```
|
||||
dotnet tool install --global dotnet-ef
|
||||
```
|
||||
Обновление:
|
||||
```
|
||||
dotnet tool update --global dotnet-ef
|
||||
```
|
||||
|
||||
## Создать миграцию
|
||||
```
|
||||
dotnet ef migrations add <MigrationName> --project AsbCloudDb
|
||||
|
@ -56,6 +56,7 @@ namespace AsbCloudInfrastructure
|
||||
services.AddTransient<ITelemetryService, TelemetryService>();
|
||||
services.AddTransient<ITelemetryUserService, TelemetryUserService>();
|
||||
services.AddTransient<ITimeZoneService, TimeZoneService>();
|
||||
services.AddTransient<IUserService, UserService>();
|
||||
services.AddTransient<IUserRoleService, UserRoleService>();
|
||||
services.AddTransient<IWellService, WellService>();
|
||||
services.AddTransient<IWellCompositeService, WellCompositeService>();
|
||||
@ -64,7 +65,6 @@ namespace AsbCloudInfrastructure
|
||||
|
||||
// admin crud services:
|
||||
services.AddTransient<ICrudService<WellDto>, CrudServiceBase<WellDto, Well>>();
|
||||
services.AddTransient<ICrudService<UserDto>, CrudServiceBase<UserDto, User>>();
|
||||
services.AddTransient<ICrudService<TelemetryDto>, CrudServiceBase<TelemetryDto, Telemetry>>();
|
||||
services.AddTransient<ICrudService<PermissionInfoDto>, CrudServiceBase<PermissionInfoDto, PermissionInfo>>();
|
||||
services.AddTransient<ICrudService<DrillParamsDto>, DrillParamsService>();
|
||||
|
@ -13,16 +13,14 @@ using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AsbCloudInfrastructure.Services.Cache;
|
||||
using Mapster;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services
|
||||
{
|
||||
public class AuthService : IAuthService
|
||||
{
|
||||
private readonly IAsbCloudDbContext db;
|
||||
private readonly CacheTable<UserRole> cacheUserRoles;
|
||||
private readonly CacheTable<RelationUserUserRole> cacheUsersUserRoles;
|
||||
private readonly CacheTable<PermissionInfo> cachePermissions;
|
||||
private readonly CacheTable<Permission> cacheUserRolesPermissions;
|
||||
private readonly IUserService userService;
|
||||
|
||||
public const string issuer = "a";
|
||||
public const string audience = "a";
|
||||
@ -37,13 +35,10 @@ namespace AsbCloudInfrastructure.Services
|
||||
private readonly HashAlgorithm hashAlgorithm;
|
||||
private readonly Random rnd;
|
||||
|
||||
public AuthService(IAsbCloudDbContext db, CacheDb cacheDb)
|
||||
public AuthService(IAsbCloudDbContext db, CacheDb cacheDb, IUserService userService)
|
||||
{
|
||||
this.db = db;
|
||||
cacheUserRoles = cacheDb.GetCachedTable<UserRole>((AsbCloudDbContext)db);
|
||||
cacheUsersUserRoles = cacheDb.GetCachedTable<RelationUserUserRole>((AsbCloudDbContext)db);
|
||||
cachePermissions = cacheDb.GetCachedTable<PermissionInfo>((AsbCloudDbContext)db);
|
||||
cacheUserRolesPermissions = cacheDb.GetCachedTable<Permission>((AsbCloudDbContext)db);
|
||||
this.userService = userService;
|
||||
hashAlgorithm = SHA384.Create();
|
||||
rnd = new Random((int)(DateTime.Now.Ticks % 2147480161));
|
||||
}
|
||||
@ -57,22 +52,13 @@ namespace AsbCloudInfrastructure.Services
|
||||
if (identity == default || user.State == 0)
|
||||
return null;
|
||||
|
||||
var userRoles = GetUserRoles(user.Id);
|
||||
|
||||
var roleNames = userRoles.Select(r => r.Caption);
|
||||
var userDto = await userService.GetAsync(user.Id, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return new UserTokenDto
|
||||
{
|
||||
Id = user.Id,
|
||||
Name = user.Name,
|
||||
CompanyName = user.Company.Caption,
|
||||
Login = user.Login,
|
||||
Patronymic = user.Patronymic,
|
||||
RoleNames = roleNames,
|
||||
Permissions = GetUserPermissions(userRoles),
|
||||
Surname = user.Surname,
|
||||
Token = MakeToken(identity.Claims),
|
||||
};
|
||||
var userTokenDto = userDto.Adapt<UserTokenDto>();
|
||||
userTokenDto.Permissions = userService.GetNestedPermissions(userDto.Id);
|
||||
userTokenDto.Token = MakeToken(identity.Claims);
|
||||
return userTokenDto;
|
||||
}
|
||||
|
||||
public string Refresh(ClaimsPrincipal user)
|
||||
@ -80,7 +66,7 @@ namespace AsbCloudInfrastructure.Services
|
||||
return MakeToken(user.Claims);
|
||||
}
|
||||
|
||||
public int Register(UserDto userDto)
|
||||
public int Register(UserRegistrationDto userDto)
|
||||
{
|
||||
if (userDto.Login is null || userDto.Login.Length is < 3 or > 50)
|
||||
return -1;
|
||||
@ -177,33 +163,12 @@ namespace AsbCloudInfrastructure.Services
|
||||
return new JwtSecurityTokenHandler().WriteToken(jwt);
|
||||
}
|
||||
|
||||
private IEnumerable<UserRole> GetUserRoles(int idUser)
|
||||
{
|
||||
var userRolesIds = cacheUsersUserRoles.Where(r =>
|
||||
r.IdUser == idUser).Select(r => r.IdUserRole);
|
||||
|
||||
return cacheUserRoles.Where(r => userRolesIds.Contains(r.Id));
|
||||
}
|
||||
|
||||
private IDictionary<string, int> GetUserPermissions(IEnumerable<UserRole> userRoles)
|
||||
{
|
||||
var rolesIds = userRoles.Select(r => r.Id);
|
||||
var userPermissionsInfo = cacheUserRolesPermissions.Where(p =>
|
||||
rolesIds.Contains(p.IdUserRole))
|
||||
.Select(perm => new { perm.IdPermission, perm.PermissionValue });
|
||||
|
||||
return userPermissionsInfo.Select(p => new
|
||||
{
|
||||
PermissionName = cachePermissions.FirstOrDefault(c => c.Id == p.IdPermission)?.Name,
|
||||
p.PermissionValue
|
||||
}).ToDictionary(k => k.PermissionName, v => v.PermissionValue);
|
||||
}
|
||||
|
||||
private async Task<(ClaimsIdentity Identity, User User)> GetClaimsUserAsync(string login,
|
||||
string password, CancellationToken token = default)
|
||||
{
|
||||
var user = await db
|
||||
.GetUsersByLogin(login)
|
||||
var user = await db.Users
|
||||
.Include(e => e.Company)
|
||||
.Where(e => e.Login == login)
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(token)
|
||||
.ConfigureAwait(false);
|
||||
@ -220,6 +185,10 @@ namespace AsbCloudInfrastructure.Services
|
||||
new (ClaimsIdentity.DefaultNameClaimType, user.Login),
|
||||
new (claimNameIdCompany, user.IdCompany.ToString()),
|
||||
};
|
||||
var roles = userService.GetRolesByIdUser(user.Id);
|
||||
if (roles is not null)
|
||||
foreach (var role in roles)
|
||||
claims.Add(new Claim(ClaimsIdentity.DefaultRoleClaimType, role.Caption));
|
||||
|
||||
var claimsIdentity = new ClaimsIdentity(claims, "Token", ClaimsIdentity.DefaultNameClaimType, ClaimsIdentity.DefaultRoleClaimType);
|
||||
return (claimsIdentity, user);
|
||||
|
@ -31,10 +31,10 @@ namespace AsbCloudInfrastructure.Services
|
||||
return dto;
|
||||
}
|
||||
|
||||
public Task<int> InsertRangeAsync(IEnumerable<PermissionDto> dtos, CancellationToken token)
|
||||
public async Task<int> InsertRangeAsync(IEnumerable<PermissionDto> dtos, CancellationToken token)
|
||||
{
|
||||
var entities = dtos.Select(Convert);
|
||||
return cachePermission.InsertAsync(entities, token);
|
||||
return (await cachePermission.InsertAsync(entities, token))?.Count()??0;
|
||||
}
|
||||
|
||||
public async Task<int> UpdateAsync(PermissionDto dto, CancellationToken token)
|
||||
@ -47,7 +47,7 @@ namespace AsbCloudInfrastructure.Services
|
||||
|
||||
public Task<int> DeleteAsync(int idUserRole, int idPermission, CancellationToken token)
|
||||
{
|
||||
bool predicate(Permission p) => p.IdUserRole == idUserRole && p.IdPermission == idPermission;
|
||||
bool predicate(Permission p) => p.IdUserRole == idUserRole && p.IdPermissionInfo == idPermission;
|
||||
return DeleteAsync(predicate, token);
|
||||
}
|
||||
|
||||
@ -75,7 +75,9 @@ namespace AsbCloudInfrastructure.Services
|
||||
public PermissionDto Convert(Permission src)
|
||||
{
|
||||
var dto = src.Adapt<PermissionDto>();
|
||||
dto.PermissionName = src.PermissionInfo?.Name;
|
||||
return dto;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -8,22 +8,42 @@ using AsbCloudDb.Model;
|
||||
using AsbCloudInfrastructure.Services.Cache;
|
||||
using Mapster;
|
||||
using AsbCloudApp.Services;
|
||||
using System;
|
||||
|
||||
namespace AsbCloudInfrastructure.Services
|
||||
{
|
||||
public class UserRoleService : IUserRoleService
|
||||
{
|
||||
private readonly CacheTable<UserRole> cacheUserRoles;
|
||||
private readonly CacheTable<PermissionInfo> cachePermissionInfo;
|
||||
private readonly IPermissionService permissionService;
|
||||
|
||||
public List<string> Includes { get; } = new();
|
||||
|
||||
public UserRoleService(IAsbCloudDbContext context, CacheDb cacheDb, IPermissionService permissionService)
|
||||
{
|
||||
cacheUserRoles = cacheDb.GetCachedTable<UserRole>((AsbCloudDbContext)context, new [] { nameof(UserRole.Permissions) });
|
||||
cachePermissionInfo = cacheDb.GetCachedTable<PermissionInfo>((AsbCloudDbContext)context);
|
||||
this.permissionService = permissionService;
|
||||
}
|
||||
|
||||
public async Task<int> InsertAsync(UserRoleDto dto, CancellationToken token = default)
|
||||
{
|
||||
var entity = dto.Adapt<UserRole>();
|
||||
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<int> InsertRangeAsync(IEnumerable<UserRoleDto> dtos, CancellationToken token = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
//var entities = dtos.Adapt<UserRole>();
|
||||
//return await cacheUserRoles.InsertAsync(entities, token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<UserRoleDto>> GetAllAsync(CancellationToken token = default)
|
||||
{
|
||||
var entities = await cacheUserRoles.WhereAsync(token)
|
||||
@ -40,60 +60,98 @@ namespace AsbCloudInfrastructure.Services
|
||||
return dto;
|
||||
}
|
||||
|
||||
public async Task<int> InsertAsync(UserRoleDto dto, CancellationToken token = default)
|
||||
public async Task<UserRoleDto> GetByNameAsync(string name, CancellationToken token = default)
|
||||
{
|
||||
var entity = dto.Adapt<UserRole>();
|
||||
var updatedEntity = await cacheUserRoles.InsertAsync(entity, token).ConfigureAwait(false);
|
||||
if (dto.Permissions?.Any() != true)
|
||||
return updatedEntity?.Id ?? 0;
|
||||
foreach (var permission in dto.Permissions)
|
||||
permission.IdUserRole = updatedEntity.Id;
|
||||
await permissionService.InsertRangeAsync(dto.Permissions, token).ConfigureAwait(false);
|
||||
await cacheUserRoles.RefreshAsync(true, token)
|
||||
var entity = await cacheUserRoles.FirstOrDefaultAsync(r => r.Caption == name, token)
|
||||
.ConfigureAwait(false);
|
||||
return updatedEntity?.Id ?? 0;
|
||||
}
|
||||
|
||||
public async Task<int> InsertRangeAsync(IEnumerable<UserRoleDto> dtos, CancellationToken token = default)
|
||||
{
|
||||
var entities = dtos.Adapt<UserRole>();
|
||||
return await cacheUserRoles.InsertAsync(entities, token).ConfigureAwait(false);
|
||||
var dto = entity?.Adapt<UserRoleDto>();
|
||||
return dto;
|
||||
}
|
||||
|
||||
public async Task<int> UpdateAsync(int id, UserRoleDto dto, CancellationToken token = default)
|
||||
{
|
||||
dto.Id = id;
|
||||
var entity = dto.Adapt<UserRole>();
|
||||
entity.Id = id;
|
||||
await UpdatePermissionsAsync(dto, token);
|
||||
|
||||
await cacheUserRoles.UpsertAsync(entity, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if(dto.Permissions is not null)
|
||||
{
|
||||
await permissionService.DeleteAllByRoleAsync(id, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (dto.Permissions.Any())
|
||||
{
|
||||
foreach (var permission in dto.Permissions)
|
||||
permission.IdUserRole = id;
|
||||
|
||||
await permissionService.InsertRangeAsync(dto.Permissions, token)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await cacheUserRoles.RefreshAsync(true, token)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
private IEnumerable<Permission> GetNestedPermissions(UserRole role, int counter = 10)
|
||||
public List<UserRoleDto> GetNestedById(int id, int recursionLevel = 7)
|
||||
{
|
||||
var role = cacheUserRoles.FirstOrDefault(r => r.Id == id);
|
||||
if (role is null)
|
||||
return null;
|
||||
var dto = role.Adapt<UserRoleDto>();
|
||||
if (role.IdParent is null || recursionLevel == 0)
|
||||
return new List<UserRoleDto> { dto };
|
||||
var parentRoles = GetNestedById((int)role.IdParent, --recursionLevel) ??
|
||||
new List<UserRoleDto>();
|
||||
parentRoles.Add(dto);
|
||||
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.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<Permission> GetNestedPermissions(UserRole role, int recursionLevel = 7)
|
||||
{
|
||||
var permissions = role.Permissions.ToList();
|
||||
if (role.IdParent is null)
|
||||
return permissions;
|
||||
if (counter == 0)
|
||||
if (recursionLevel == 0)
|
||||
{
|
||||
Trace.WriteLine($"User role with id: {role.Id} has more than 10 nested childs");
|
||||
return permissions;
|
||||
@ -103,29 +161,9 @@ namespace AsbCloudInfrastructure.Services
|
||||
if (parentRole is null)
|
||||
return permissions;
|
||||
|
||||
var parentPermissions = GetNestedPermissions(parentRole, --counter);
|
||||
Merge(ref permissions, parentPermissions);
|
||||
return permissions;
|
||||
}
|
||||
|
||||
private static void Merge(ref List<Permission> permissions, IEnumerable<Permission> newPermissions)
|
||||
{
|
||||
foreach (var newPermission in newPermissions)
|
||||
{
|
||||
var permissionIndex = permissions.FindIndex(p => p.IdPermission == newPermission.IdPermission);
|
||||
if (permissionIndex == -1)
|
||||
permissions.Add(newPermission);
|
||||
else
|
||||
{
|
||||
var permission = permissions[permissionIndex];
|
||||
permissions[permissionIndex] = new Permission
|
||||
{
|
||||
IdPermission = permission.IdPermission,
|
||||
IdUserRole = permission.IdUserRole,
|
||||
PermissionValue = permission.PermissionValue | newPermission.PermissionValue,
|
||||
};
|
||||
}
|
||||
}
|
||||
var parentPermissions = GetNestedPermissions(parentRole, --recursionLevel);
|
||||
|
||||
return permissions.Union(parentPermissions);
|
||||
}
|
||||
|
||||
public Task<int> DeleteAsync(int id, CancellationToken token = default)
|
||||
@ -133,5 +171,35 @@ namespace AsbCloudInfrastructure.Services
|
||||
|
||||
public Task<int> DeleteAsync(IEnumerable<int> ids, CancellationToken token = default)
|
||||
=> cacheUserRoles.RemoveAsync(r => ids.Contains(r.Id), token);
|
||||
|
||||
public bool HasPermission(IEnumerable<int> 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;
|
||||
}
|
||||
}
|
||||
}
|
166
AsbCloudInfrastructure/Services/UserService.cs
Normal file
166
AsbCloudInfrastructure/Services/UserService.cs
Normal file
@ -0,0 +1,166 @@
|
||||
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 UserService : IUserService
|
||||
{
|
||||
private readonly CacheTable<User> cacheUsers;
|
||||
private readonly CacheTable<RelationUserUserRole> cacheRelationUserToRoles;
|
||||
|
||||
public List<string> Includes { get; }
|
||||
public IUserRoleService RoleService { get; }
|
||||
|
||||
public UserService(IAsbCloudDbContext context, CacheDb cacheDb, IUserRoleService roleService)
|
||||
{
|
||||
var db = (AsbCloudDbContext)context;
|
||||
cacheUsers = cacheDb.GetCachedTable<User>(
|
||||
db,
|
||||
new[] {
|
||||
nameof(User.RelationUsersUserRoles),
|
||||
nameof(User.Company),
|
||||
});
|
||||
cacheRelationUserToRoles = cacheDb.GetCachedTable<RelationUserUserRole>(
|
||||
db,
|
||||
new[] {
|
||||
nameof(RelationUserUserRole.User),
|
||||
nameof(RelationUserUserRole.UserRole),
|
||||
});
|
||||
RoleService = roleService;
|
||||
}
|
||||
|
||||
public async Task<int> InsertAsync(UserExtendedDto dto, CancellationToken token = default)
|
||||
{
|
||||
var entity = dto.Adapt<User>();
|
||||
var updatedEntity = await cacheUsers.InsertAsync(entity, token).ConfigureAwait(false);
|
||||
await UpdateRolesCacheForUserAsync((int)updatedEntity.Id, dto.RoleNames, token);
|
||||
return updatedEntity?.Id ?? 0;
|
||||
}
|
||||
|
||||
public Task<int> InsertRangeAsync(IEnumerable<UserExtendedDto> newItems, CancellationToken token = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<UserExtendedDto>> GetAllAsync(CancellationToken token = default)
|
||||
{
|
||||
var entities = (await cacheUsers.WhereAsync(token).ConfigureAwait(false))
|
||||
.ToList();
|
||||
if (entities.Count == 0)
|
||||
return null;
|
||||
var dtos = entities.Adapt<UserExtendedDto>().ToList();
|
||||
for (var i = 0; i < dtos.Count; i++)
|
||||
dtos[i].RoleNames = GetRolesNamesByIdUser(dtos[i].Id);
|
||||
|
||||
return dtos;
|
||||
}
|
||||
|
||||
public async Task<UserExtendedDto> GetAsync(int id, CancellationToken token = default)
|
||||
{
|
||||
var entity = await cacheUsers.FirstOrDefaultAsync(u=>u.Id == id, token).ConfigureAwait(false);
|
||||
var dto = entity.Adapt<UserExtendedDto>();
|
||||
dto.RoleNames = GetRolesNamesByIdUser(dto.Id);
|
||||
return dto;
|
||||
}
|
||||
|
||||
public async Task<int> UpdateAsync(int id, UserExtendedDto dto, CancellationToken token = default)
|
||||
{
|
||||
var entity = dto.Adapt<User>();
|
||||
await UpdateRolesCacheForUserAsync(id, dto.RoleNames, token);
|
||||
|
||||
var result = await cacheUsers.UpsertAsync(entity, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Task<int> DeleteAsync(int id, CancellationToken token = default)
|
||||
=> cacheUsers.RemoveAsync(r => r.Id == id, token);
|
||||
|
||||
public Task<int> DeleteAsync(IEnumerable<int> ids, CancellationToken token = default)
|
||||
=> cacheUsers.RemoveAsync(r => ids.Contains(r.Id), token);
|
||||
|
||||
private IEnumerable<string> GetRolesNamesByIdUser(int idUser)
|
||||
=> GetRolesByIdUser(idUser)
|
||||
?.Select(r => r.Caption)
|
||||
.Distinct();
|
||||
|
||||
public IEnumerable<UserRoleDto> GetRolesByIdUser(int idUser)
|
||||
{
|
||||
var roles = cacheRelationUserToRoles.Where(r => r.IdUser == idUser);
|
||||
if (roles?.Any() != true)
|
||||
return null;
|
||||
return roles.SelectMany(r => RoleService.GetNestedById(r.IdUserRole));
|
||||
}
|
||||
|
||||
public IEnumerable<PermissionBaseDto> GetNestedPermissions(int idUser)
|
||||
{
|
||||
var roles = GetRolesByIdUser(idUser);
|
||||
return RoleService.GetNestedPermissions(roles);
|
||||
}
|
||||
|
||||
private async Task UpdateRolesCacheForUserAsync(int idUser, IEnumerable<string> newRoleNames, CancellationToken token)
|
||||
{
|
||||
if (newRoleNames?.Any() != true)
|
||||
return;
|
||||
|
||||
var relatrions = new List<RelationUserUserRole>(newRoleNames.Count());
|
||||
foreach (var roleName in newRoleNames)
|
||||
{
|
||||
var role = await RoleService.GetByNameAsync(roleName, token)
|
||||
.ConfigureAwait(false);
|
||||
if (role != null)
|
||||
relatrions.Add(new()
|
||||
{
|
||||
IdUser = idUser,
|
||||
IdUserRole = role.Id,
|
||||
});
|
||||
}
|
||||
await cacheRelationUserToRoles.RemoveAsync(r => r.IdUser == idUser, token)
|
||||
.ConfigureAwait(false);
|
||||
await cacheRelationUserToRoles.InsertAsync(relatrions, token)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public bool HasAnyRoleOf(int idUser, IEnumerable<string> roleNames)
|
||||
{
|
||||
if(!roleNames.Any())
|
||||
return true;
|
||||
var userRoleNames = GetRolesNamesByIdUser(idUser);
|
||||
foreach (var roleName in userRoleNames)
|
||||
if (roleNames.Contains(roleName))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool HasAnyRoleOf(int idUser, IEnumerable<int> roleIds)
|
||||
{
|
||||
if (!roleIds.Any())
|
||||
return true;
|
||||
var userRoles = GetRolesByIdUser(idUser);
|
||||
foreach (var role in userRoles)
|
||||
if (roleIds.Contains(role.Id))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool HasPermission(int idUser, string permissionName, int permissionMask = 0)
|
||||
{
|
||||
var relationsToRoles = cacheRelationUserToRoles.Where(r=>r.IdUser == idUser);
|
||||
if (relationsToRoles is null)
|
||||
return false;
|
||||
|
||||
return RoleService.HasPermission(relationsToRoles.Select(r => r.IdUserRole),
|
||||
permissionName,
|
||||
permissionMask);
|
||||
}
|
||||
}
|
||||
}
|
@ -8,12 +8,12 @@ namespace AsbCloudWebApi.Controllers
|
||||
[Route("api/admin/user")]
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
public class AdminUserController : CrudController<UserDto, ICrudService<UserDto>>
|
||||
public class AdminUserController : CrudController<UserExtendedDto, ICrudService<UserExtendedDto>>
|
||||
{
|
||||
public AdminUserController(ICrudService<UserDto> service)
|
||||
: base(service)
|
||||
{
|
||||
public AdminUserController(IUserService service)
|
||||
:base(service)
|
||||
{}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ namespace AsbCloudWebApi.Controllers
|
||||
/// <param name="user">Информация о новом пользователе</param>
|
||||
/// <returns code="200">Ок</returns>
|
||||
[HttpPost]
|
||||
public IActionResult Register(UserDto user)
|
||||
public IActionResult Register(UserRegistrationDto user)
|
||||
{
|
||||
var code = authService.Register(user);
|
||||
return code switch
|
||||
|
@ -1,8 +1,5 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AsbCloudWebApi.Middlewares
|
||||
|
Loading…
Reference in New Issue
Block a user