diff --git a/PARR.API/Contracts/V1/ApiRoutes.cs b/PARR.API/Contracts/V1/ApiRoutes.cs
index 802c354d..aa1f957a 100644
--- a/PARR.API/Contracts/V1/ApiRoutes.cs
+++ b/PARR.API/Contracts/V1/ApiRoutes.cs
@@ -136,6 +136,16 @@ namespace PARR.API.Contracts.V1
public const string getParam = "{id}";
}
+ public static class UserRoles
+ {
+ public const string Get = Base + "/users/" + userParam + "/roles/";
+ public const string AddRole = Base + "/users/" + userParam + "/roles/";
+ public const string DeleteRole = Base + "/users/" + userParam + "/roles/" + roleParam;
+
+ public const string userParam = "{userId}";
+ public const string roleParam = "{roleId}";
+ }
+
public static class Role
{
public const string GetAll = Base + "/roles/";
diff --git a/PARR.API/Contracts/V1/Requests/UserAddRoleRequest.cs b/PARR.API/Contracts/V1/Requests/UserAddRoleRequest.cs
new file mode 100644
index 00000000..8d47b36f
--- /dev/null
+++ b/PARR.API/Contracts/V1/Requests/UserAddRoleRequest.cs
@@ -0,0 +1,7 @@
+namespace PARR.API.Contracts.V1.Requests
+{
+ public class UserAddRoleRequest
+ {
+ public Guid RoleId { get; set; }
+ }
+}
diff --git a/PARR.API/Controllers/V1/UserRoleController.cs b/PARR.API/Controllers/V1/UserRoleController.cs
new file mode 100644
index 00000000..a8355b0f
--- /dev/null
+++ b/PARR.API/Controllers/V1/UserRoleController.cs
@@ -0,0 +1,148 @@
+using AutoMapper;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+using PARR.API.Contracts.V1;
+using PARR.API.Contracts.V1.Requests;
+using PARR.API.Contracts.V1.Responses;
+using PARR.API.Contracts.V1.Responses.Base;
+using PARR.API.Controllers.V1.Base;
+using PARR.API.Services.Interfaces;
+using PARR.Constants;
+using PARR.DAL.Models;
+using PARR.DAL.Services.Interfaces;
+
+namespace PARR.API.Controllers.V1
+{
+ ///
+ /// Управление ролями пользователя
+ ///
+ [Authorize(Roles = ParrRoles.Administrator.Role)]
+ public class UserRoleController : BaseApiController
+ {
+ private readonly IUserService userService;
+ private readonly IMapper mapper;
+ private readonly ILogger logger;
+ private readonly IRoleService roleService;
+ private readonly IAuthService authService;
+ private readonly IUriService uriService;
+
+ public UserRoleController(
+ IUserService userService,
+ IMapper mapper,
+ ILogger logger,
+ IRoleService roleService,
+ IAuthService authService,
+ IUriService uriService
+ )
+ {
+ this.userService = userService;
+ this.mapper = mapper;
+ this.logger = logger;
+ this.roleService = roleService;
+ this.authService = authService;
+ this.uriService = uriService;
+ }
+
+
+ ///
+ /// Роли пользователя
+ ///
+ ///
+ ///
+ [HttpGet(ApiRoutes.UserRoles.Get)]
+ public async Task Get([FromRoute] Guid userId)
+ {
+ var user = await userService.Get()
+ .Include(t => t.Roles).ThenInclude(t => t.Role)
+ .FirstOrDefaultAsync(t => t.Id == userId);
+
+ if (user == null)
+ return BadRequest(new Response(false, new List { new ErrorModel { Message = $"Не найден пользователь с id: {userId}" } }));
+
+ var response = mapper.Map>(user.Roles.Select(t => t.Role)).OrderBy(t => t.Description).ToList();
+
+ return Ok(new Response>(response, true));
+ }
+
+
+ ///
+ /// Добавить роль пользователю
+ ///
+ ///
+ ///
+ [HttpPost(ApiRoutes.UserRoles.AddRole)]
+ public async Task AddRole([FromRoute] Guid userId, [FromBody] UserAddRoleRequest request)
+ {
+ var user = await userService.Get()
+ .Include(t => t.Roles)
+ .FirstOrDefaultAsync(t => t.Id == userId);
+
+ if (user == null)
+ return BadRequest(new Response(false, new List { new ErrorModel { Message = $"Не найден пользователь с id: {userId}" } }));
+
+ if (user.Roles.FirstOrDefault(t => t.RoleId == request.RoleId) != null)
+ return BadRequest(new Response(false, new List { new ErrorModel { Message = $"У пользователя {userId} уже есть роль {request.RoleId}" } }));
+
+ var newRole = await roleService.GetAsync(request.RoleId);
+ if (newRole == null)
+ return BadRequest(new Response(false, new List { new ErrorModel { Message = $"Не найдена роль с id: {request.RoleId}" } }));
+
+ // добавляем роль
+ user.Roles.Add(new UsersInRole { RoleId = newRole.Id });
+ if (!await userService.CommitAsync())
+ return BadRequest(new Response(false, new List { new ErrorModel { Message = $"Ошибка при добавлении роли {request.RoleId} пользователю {userId}" } }));
+
+ logger.LogInformation($"Пользователь {User.Identity?.Name} добвавил роль {newRole.Name} пользователю {userId}, {user.Ip}, {user.Name}");
+
+ // удалить из кэша
+ await authService.RemoveCacheUserAsync(user.Ip);
+
+
+ var updatedUser = await userService.Get()
+ .Include(t => t.Roles).ThenInclude(t => t.Role)
+ .FirstAsync(t => t.Id == userId);
+
+ var response = mapper.Map>(updatedUser.Roles.Select(t => t.Role)).OrderBy(t => t.Description).ToList();
+ var locationUri = uriService.GetUri(ApiRoutes.UserRoles.Get, ApiRoutes.UserRoles.userParam, userId);
+
+ return Created(locationUri, new Response>(response, true));
+ }
+
+
+ ///
+ /// Удалить роль у пользователя
+ ///
+ ///
+ ///
+ ///
+ [HttpDelete(ApiRoutes.UserRoles.DeleteRole)]
+ public async Task DeleteRole([FromRoute] Guid userId, [FromRoute] Guid roleId)
+ {
+ var user = await userService.Get()
+ .Include(t => t.Roles).ThenInclude(t => t.Role)
+ .FirstOrDefaultAsync(t => t.Id == userId);
+
+ if (user == null)
+ return BadRequest(new Response(false, new List { new ErrorModel { Message = $"Не найден пользователь с id: {userId}" } }));
+
+ var userRole = user.Roles.FirstOrDefault(t => t.RoleId == roleId);
+
+ if (userRole == null)
+ return BadRequest(new Response(false, new List { new ErrorModel { Message = $"У пользователя {userId} нет роли с id {roleId}" } }));
+
+ user.Roles.Remove(userRole);
+
+ if (!await userService.CommitAsync())
+ return BadRequest(new Response(false, new List { new ErrorModel { Message = $"Ошибка при удалении роли {roleId} у пользователя {userId}" } }));
+
+ logger.LogInformation($"Пользователь {User.Identity?.Name} удалил роль {userRole.Role.Name} у пользователя {userId}, {user.Ip}, {user.Name}");
+
+ // удалить из кэша
+ await authService.RemoveCacheUserAsync(user.Ip);
+
+ return NoContent();
+ }
+
+ }
+}
diff --git a/PARR.API/Services/Implementations/AuthService.cs b/PARR.API/Services/Implementations/AuthService.cs
index 901e62be..da31cb08 100644
--- a/PARR.API/Services/Implementations/AuthService.cs
+++ b/PARR.API/Services/Implementations/AuthService.cs
@@ -85,6 +85,19 @@ namespace PARR.API.Services.Implementations
}
+ public async Task UnlockUserAsync(string ipAddress)
+ {
+ await cacheService.DeleteCachedDataAsync(GetBlockKey(ipAddress));
+ }
+
+
+ public async Task RemoveCacheUserAsync(string ipAddress)
+ {
+ await cacheService.DeleteCachedDataAsync(GetAllowKey(ipAddress));
+ await cacheService.DeleteCachedDataAsync(GetBlockKey(ipAddress));
+ }
+
+
private string GetBlockKey(string ipAddress)
{
// d - deny
diff --git a/PARR.API/Services/Interfaces/IAuthService.cs b/PARR.API/Services/Interfaces/IAuthService.cs
index 161133d9..d126cda9 100644
--- a/PARR.API/Services/Interfaces/IAuthService.cs
+++ b/PARR.API/Services/Interfaces/IAuthService.cs
@@ -18,5 +18,19 @@ namespace PARR.API.Services.Interfaces
///
///
Task UserIsBlockedAsync(string ipAddress);
+
+ ///
+ /// Удалить из кэша пользователя
+ ///
+ ///
+ ///
+ Task RemoveCacheUserAsync(string ipAddress);
+
+ ///
+ /// Разблокировать пользователя
+ ///
+ ///
+ ///
+ Task UnlockUserAsync(string ipAddress);
}
}
diff --git a/PARR.DAL/CacheServices/IRedisCacheService.cs b/PARR.DAL/CacheServices/IRedisCacheService.cs
index cc92cc52..4176205f 100644
--- a/PARR.DAL/CacheServices/IRedisCacheService.cs
+++ b/PARR.DAL/CacheServices/IRedisCacheService.cs
@@ -2,12 +2,52 @@
{
public interface IRedisCacheService
{
+ ///
+ /// Получить кэшированные данные
+ ///
+ ///
+ ///
+ ///
T? GetCachedData(string key);
+ ///
+ /// Получить кэшированные данные асинхронно
+ ///
+ ///
+ ///
+ ///
Task GetCachedDataAsync(string key);
+ ///
+ /// Добавить в кэш данные
+ ///
+ ///
+ ///
+ ///
+ ///
void SetCachedData(string key, T data, TimeSpan cacheDuration);
+ ///
+ /// Добавить в кэш данные асинхронно
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
Task SetCachedDataAsync(string key, T data, TimeSpan cacheDuration);
+
+ ///
+ /// Удалить кэшированные данные
+ ///
+ ///
+ void DeleteCachedData(string key);
+
+ ///
+ /// Удалить кэшированные данные асинхронно
+ ///
+ ///
+ ///
+ Task DeleteCachedDataAsync(string key);
}
}
diff --git a/PARR.DAL/CacheServices/RedisCacheService.cs b/PARR.DAL/CacheServices/RedisCacheService.cs
index abf94815..352462d1 100644
--- a/PARR.DAL/CacheServices/RedisCacheService.cs
+++ b/PARR.DAL/CacheServices/RedisCacheService.cs
@@ -15,7 +15,7 @@ namespace PARR.DAL.CacheServices
public async Task GetCachedDataAsync(string key)
{
var jsonData = await cache.GetStringAsync(key);
-
+
if (jsonData == null)
return default(T);
@@ -58,6 +58,16 @@ namespace PARR.DAL.CacheServices
}
+ public async Task DeleteCachedDataAsync(string key)
+ {
+ await cache.RemoveAsync(key);
+ }
+
+
+ public void DeleteCachedData(string key)
+ {
+ cache.Remove(key);
+ }
}
}