Refactor authorization folder structure
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
using Distribution.Features.User;
|
||||
using Distribution.Infra.Auth;
|
||||
using Distribution.Infra.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Distribution.Features.Auth;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Distribution.Infra.Auth;
|
||||
using Distribution.Infra.Authorization;
|
||||
using Distribution.Infra.UoW;
|
||||
|
||||
namespace Distribution.Features.Order;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Distribution.Infra.Auth;
|
||||
using Distribution.Infra.Authorization;
|
||||
using Distribution.Infra.UoW;
|
||||
|
||||
namespace Distribution.Features.Store;
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Distribution.Infra.Auth
|
||||
{
|
||||
public class CurrentUserService : ICurrentUserService
|
||||
{
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
public CurrentUserService(IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
}
|
||||
|
||||
private System.Security.Claims.ClaimsPrincipal? User => _httpContextAccessor.HttpContext?.User;
|
||||
|
||||
public bool IsAuthenticated => User?.Identity?.IsAuthenticated ?? false;
|
||||
|
||||
public Guid? UserId
|
||||
{
|
||||
get
|
||||
{
|
||||
var value = User?.FindFirst("sub")?.Value ?? User?.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
|
||||
return Guid.TryParse(value, out var id) ? id : null;
|
||||
}
|
||||
}
|
||||
|
||||
public Guid? RoleId
|
||||
{
|
||||
get
|
||||
{
|
||||
var value = User?.FindFirst("role_id")?.Value;
|
||||
return Guid.TryParse(value, out var id) ? id : null;
|
||||
}
|
||||
}
|
||||
|
||||
public string? Username => User?.FindFirst("username")?.Value;
|
||||
|
||||
public int RoleLevel
|
||||
{
|
||||
get
|
||||
{
|
||||
var value = User?.FindFirst("role_level")?.Value;
|
||||
return int.TryParse(value, out var level) ? level : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
namespace Distribution.Infra.Auth
|
||||
{
|
||||
public interface ICurrentUserService
|
||||
{
|
||||
Guid? UserId { get; }
|
||||
Guid? RoleId { get; }
|
||||
string? Username { get; }
|
||||
int RoleLevel { get; }
|
||||
bool IsAuthenticated { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace Distribution.Infra.Auth
|
||||
{
|
||||
public interface IJwtTokenService
|
||||
{
|
||||
string GenerateToken(Models.User user);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace Distribution.Infra.Auth
|
||||
{
|
||||
public interface IPermissionService
|
||||
{
|
||||
Task<bool> HasPermissionAsync(Guid roleId, string permissionCode);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
namespace Distribution.Infra.Auth
|
||||
{
|
||||
public interface IScopeAccessService
|
||||
{
|
||||
Task<bool> HasStoreScopeAsync(Guid userId, Guid storeId);
|
||||
Task<bool> HasRouteScopeAsync(Guid userId, Guid routeId);
|
||||
Task<bool> HasRegionScopeAsync(Guid userId, Guid regionId);
|
||||
Task<List<Guid>> GetAccessibleStoreIdsAsync(Guid userId);
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
|
||||
namespace Distribution.Infra.Auth
|
||||
{
|
||||
public class JwtTokenService : IJwtTokenService
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
public JwtTokenService(IConfiguration configuration)
|
||||
{
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
public string GenerateToken(Models.User user)
|
||||
{
|
||||
var jwtSection = _configuration.GetSection("Jwt");
|
||||
var key = jwtSection["Key"] ?? throw new InvalidOperationException("Jwt:Key is not configured.");
|
||||
var issuer = jwtSection["Issuer"];
|
||||
var audience = jwtSection["Audience"];
|
||||
var expiryMinutes = int.TryParse(jwtSection["ExpiryMinutes"], out var m) ? m : 120;
|
||||
|
||||
var claims = new List<Claim>
|
||||
{
|
||||
new("sub", user.Id.ToString()),
|
||||
new("username", user.Username),
|
||||
new("role", user.Role?.Name ?? string.Empty),
|
||||
new("role_id", user.FK_RoleId.ToString()),
|
||||
new("role_level", (user.Role?.Level ?? 0).ToString())
|
||||
};
|
||||
|
||||
var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
|
||||
var credentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);
|
||||
|
||||
var token = new JwtSecurityToken(
|
||||
issuer: issuer,
|
||||
audience: audience,
|
||||
claims: claims,
|
||||
expires: DateTime.Now.AddMinutes(expiryMinutes),
|
||||
signingCredentials: credentials);
|
||||
|
||||
return new JwtSecurityTokenHandler().WriteToken(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace Distribution.Infra.Auth
|
||||
{
|
||||
public class PermissionAuthorizationHandler : AuthorizationHandler<PermissionRequirement>
|
||||
{
|
||||
private readonly IPermissionService _permissionService;
|
||||
|
||||
public PermissionAuthorizationHandler(IPermissionService permissionService)
|
||||
{
|
||||
_permissionService = permissionService;
|
||||
}
|
||||
|
||||
protected override async Task HandleRequirementAsync(
|
||||
AuthorizationHandlerContext context, PermissionRequirement requirement)
|
||||
{
|
||||
var roleIdClaim = context.User.FindFirst("role_id")?.Value;
|
||||
if (!Guid.TryParse(roleIdClaim, out var roleId))
|
||||
return;
|
||||
|
||||
if (await _permissionService.HasPermissionAsync(roleId, requirement.PermissionCode))
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Distribution.Infra.Auth
|
||||
{
|
||||
public class PermissionPolicyProvider : IAuthorizationPolicyProvider
|
||||
{
|
||||
private readonly DefaultAuthorizationPolicyProvider _fallback;
|
||||
|
||||
public PermissionPolicyProvider(IOptions<AuthorizationOptions> options)
|
||||
{
|
||||
_fallback = new DefaultAuthorizationPolicyProvider(options);
|
||||
}
|
||||
|
||||
public Task<AuthorizationPolicy> GetDefaultPolicyAsync() => _fallback.GetDefaultPolicyAsync();
|
||||
|
||||
public Task<AuthorizationPolicy?> GetFallbackPolicyAsync() => _fallback.GetFallbackPolicyAsync();
|
||||
|
||||
public Task<AuthorizationPolicy?> GetPolicyAsync(string policyName)
|
||||
{
|
||||
if (policyName.Contains('.'))
|
||||
{
|
||||
var policy = new AuthorizationPolicyBuilder()
|
||||
.RequireAuthenticatedUser()
|
||||
.AddRequirements(new PermissionRequirement(policyName))
|
||||
.Build();
|
||||
return Task.FromResult<AuthorizationPolicy?>(policy);
|
||||
}
|
||||
|
||||
return _fallback.GetPolicyAsync(policyName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace Distribution.Infra.Auth
|
||||
{
|
||||
public class PermissionRequirement : IAuthorizationRequirement
|
||||
{
|
||||
public string PermissionCode { get; }
|
||||
|
||||
public PermissionRequirement(string permissionCode)
|
||||
{
|
||||
PermissionCode = permissionCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
using Distribution.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Distribution.Infra.Auth
|
||||
{
|
||||
public class PermissionService : IPermissionService
|
||||
{
|
||||
private readonly AppDBContext _context;
|
||||
|
||||
public PermissionService(AppDBContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<bool> HasPermissionAsync(Guid roleId, string permissionCode) =>
|
||||
await _context.RolePermissions
|
||||
.Where(rp => rp.FK_RoleId == roleId && rp.IsActive)
|
||||
.Join(_context.Permissions.Where(p => p.IsActive), rp => rp.FK_PermissionId, p => p.Id, (rp, p) => p.Code)
|
||||
.AnyAsync(code => code == permissionCode);
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
using Distribution.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Distribution.Infra.Auth
|
||||
{
|
||||
public class ScopeAccessService : IScopeAccessService
|
||||
{
|
||||
private readonly AppDBContext _context;
|
||||
|
||||
public ScopeAccessService(AppDBContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<bool> HasStoreScopeAsync(Guid userId, Guid storeId) =>
|
||||
await _context.UserStoreScopes.AnyAsync(s => s.FK_UserId == userId && s.FK_StoreId == storeId && s.IsActive);
|
||||
|
||||
public async Task<bool> HasRouteScopeAsync(Guid userId, Guid routeId) =>
|
||||
await _context.UserRouteScopes.AnyAsync(s => s.FK_UserId == userId && s.FK_RouteId == routeId && s.IsActive);
|
||||
|
||||
public async Task<bool> HasRegionScopeAsync(Guid userId, Guid regionId) =>
|
||||
await _context.UserRegionScopes.AnyAsync(s => s.FK_UserId == userId && s.FK_RegionId == regionId && s.IsActive);
|
||||
|
||||
public async Task<List<Guid>> GetAccessibleStoreIdsAsync(Guid userId)
|
||||
{
|
||||
var directStoreIds = await _context.UserStoreScopes
|
||||
.Where(s => s.FK_UserId == userId && s.IsActive)
|
||||
.Select(s => s.FK_StoreId)
|
||||
.ToListAsync();
|
||||
|
||||
var routeIds = await _context.UserRouteScopes
|
||||
.Where(s => s.FK_UserId == userId && s.IsActive)
|
||||
.Select(s => s.FK_RouteId)
|
||||
.ToListAsync();
|
||||
|
||||
var regionIds = await _context.UserRegionScopes
|
||||
.Where(s => s.FK_UserId == userId && s.IsActive)
|
||||
.Select(s => s.FK_RegionId)
|
||||
.ToListAsync();
|
||||
|
||||
var storeIdsViaRoute = await _context.Stores
|
||||
.Where(s => routeIds.Contains(s.FK_RouteId) && s.IsActive)
|
||||
.Select(s => s.Id)
|
||||
.ToListAsync();
|
||||
|
||||
var routeIdsViaRegion = await _context.Routes
|
||||
.Where(r => regionIds.Contains(r.FK_RegionId) && r.IsActive)
|
||||
.Select(r => r.Id)
|
||||
.ToListAsync();
|
||||
|
||||
var storeIdsViaRegion = await _context.Stores
|
||||
.Where(s => routeIdsViaRegion.Contains(s.FK_RouteId) && s.IsActive)
|
||||
.Select(s => s.Id)
|
||||
.ToListAsync();
|
||||
|
||||
return directStoreIds
|
||||
.Concat(storeIdsViaRoute)
|
||||
.Concat(storeIdsViaRegion)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Distribution.Infra.Authorization;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/authorization")]
|
||||
[Authorize]
|
||||
public class AuthorizationController(
|
||||
IPermissionService permissionService,
|
||||
IScopeAccessService scopeAccessService,
|
||||
ICurrentUserService currentUser) : ControllerBase
|
||||
{
|
||||
[HttpGet("permissions/{permissionCode}")]
|
||||
public async Task<IActionResult> CheckPermission(string permissionCode)
|
||||
{
|
||||
if (currentUser.RoleId is not { } roleId) return Unauthorized();
|
||||
var result = await permissionService.HasPermissionAsync(roleId, permissionCode);
|
||||
return Ok(new PermissionCheckResponse(result));
|
||||
}
|
||||
|
||||
[HttpGet("scopes/stores/{storeId:guid}")]
|
||||
public async Task<IActionResult> CheckStoreScope(Guid storeId)
|
||||
{
|
||||
if (currentUser.UserId is not { } userId) return Unauthorized();
|
||||
var result = await scopeAccessService.HasStoreScopeAsync(userId, storeId);
|
||||
return Ok(new StoreScopeCheckResponse(result));
|
||||
}
|
||||
|
||||
[HttpGet("scopes/routes/{routeId:guid}")]
|
||||
public async Task<IActionResult> CheckRouteScope(Guid routeId)
|
||||
{
|
||||
if (currentUser.UserId is not { } userId) return Unauthorized();
|
||||
var result = await scopeAccessService.HasRouteScopeAsync(userId, routeId);
|
||||
return Ok(new RouteScopeCheckResponse(result));
|
||||
}
|
||||
|
||||
[HttpGet("scopes/regions/{regionId:guid}")]
|
||||
public async Task<IActionResult> CheckRegionScope(Guid regionId)
|
||||
{
|
||||
if (currentUser.UserId is not { } userId) return Unauthorized();
|
||||
var result = await scopeAccessService.HasRegionScopeAsync(userId, regionId);
|
||||
return Ok(new RegionScopeCheckResponse(result));
|
||||
}
|
||||
|
||||
[HttpGet("scopes/stores")]
|
||||
public async Task<IActionResult> GetAccessibleStores()
|
||||
{
|
||||
if (currentUser.UserId is not { } userId) return Unauthorized();
|
||||
var result = await scopeAccessService.GetAccessibleStoreIdsAsync(userId);
|
||||
return Ok(new AccessibleStoresResponse(result));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Distribution.Infra.Authorization;
|
||||
|
||||
public class CurrentUserService : ICurrentUserService
|
||||
{
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
public CurrentUserService(IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
}
|
||||
|
||||
private System.Security.Claims.ClaimsPrincipal? User => _httpContextAccessor.HttpContext?.User;
|
||||
|
||||
public bool IsAuthenticated => User?.Identity?.IsAuthenticated ?? false;
|
||||
|
||||
public Guid? UserId
|
||||
{
|
||||
get
|
||||
{
|
||||
var value = User?.FindFirst("sub")?.Value ?? User?.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
|
||||
return Guid.TryParse(value, out var id) ? id : null;
|
||||
}
|
||||
}
|
||||
|
||||
public Guid? RoleId
|
||||
{
|
||||
get
|
||||
{
|
||||
var value = User?.FindFirst("role_id")?.Value;
|
||||
return Guid.TryParse(value, out var id) ? id : null;
|
||||
}
|
||||
}
|
||||
|
||||
public string? Username => User?.FindFirst("username")?.Value;
|
||||
|
||||
public int RoleLevel
|
||||
{
|
||||
get
|
||||
{
|
||||
var value = User?.FindFirst("role_level")?.Value;
|
||||
return int.TryParse(value, out var level) ? level : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Distribution.Infra.Authorization;
|
||||
|
||||
public interface ICurrentUserService
|
||||
{
|
||||
Guid? UserId { get; }
|
||||
Guid? RoleId { get; }
|
||||
string? Username { get; }
|
||||
int RoleLevel { get; }
|
||||
bool IsAuthenticated { get; }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace Distribution.Infra.Authorization;
|
||||
|
||||
public record PermissionCheckResponse(bool HasPermission);
|
||||
|
||||
public record StoreScopeCheckResponse(bool HasScope);
|
||||
|
||||
public record RouteScopeCheckResponse(bool HasScope);
|
||||
|
||||
public record RegionScopeCheckResponse(bool HasScope);
|
||||
|
||||
public record AccessibleStoresResponse(List<Guid> StoreIds);
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Distribution.Infra.Authorization;
|
||||
|
||||
public interface IJwtTokenService
|
||||
{
|
||||
string GenerateToken(Models.User user);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
|
||||
namespace Distribution.Infra.Authorization;
|
||||
|
||||
public class JwtTokenService : IJwtTokenService
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
public JwtTokenService(IConfiguration configuration)
|
||||
{
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
public string GenerateToken(Models.User user)
|
||||
{
|
||||
var jwtSection = _configuration.GetSection("Jwt");
|
||||
var key = jwtSection["Key"] ?? throw new InvalidOperationException("Jwt:Key is not configured.");
|
||||
var issuer = jwtSection["Issuer"];
|
||||
var audience = jwtSection["Audience"];
|
||||
var expiryMinutes = int.TryParse(jwtSection["ExpiryMinutes"], out var m) ? m : 120;
|
||||
|
||||
var claims = new List<Claim>
|
||||
{
|
||||
new("sub", user.Id.ToString()),
|
||||
new("username", user.Username),
|
||||
new("role", user.Role?.Name ?? string.Empty),
|
||||
new("role_id", user.FK_RoleId.ToString()),
|
||||
new("role_level", (user.Role?.Level ?? 0).ToString())
|
||||
};
|
||||
|
||||
var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
|
||||
var credentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);
|
||||
|
||||
var token = new JwtSecurityToken(
|
||||
issuer: issuer,
|
||||
audience: audience,
|
||||
claims: claims,
|
||||
expires: DateTime.Now.AddMinutes(expiryMinutes),
|
||||
signingCredentials: credentials);
|
||||
|
||||
return new JwtSecurityTokenHandler().WriteToken(token);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Distribution.Infra.Authorization;
|
||||
|
||||
public interface IPermissionRepository
|
||||
{
|
||||
Task<bool> HasPermissionAsync(Guid roleId, string permissionCode);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Distribution.Infra.Authorization;
|
||||
|
||||
public interface IPermissionService
|
||||
{
|
||||
Task<bool> HasPermissionAsync(Guid roleId, string permissionCode);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using Distribution.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Distribution.Infra.Authorization;
|
||||
|
||||
public class PermissionRepository(AppDBContext context) : IPermissionRepository
|
||||
{
|
||||
public async Task<bool> HasPermissionAsync(Guid roleId, string permissionCode) =>
|
||||
await context.RolePermissions
|
||||
.Where(rp => rp.FK_RoleId == roleId && rp.IsActive)
|
||||
.Join(context.Permissions.Where(p => p.IsActive), rp => rp.FK_PermissionId, p => p.Id, (rp, p) => p.Code)
|
||||
.AnyAsync(code => code == permissionCode);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Distribution.Infra.Authorization;
|
||||
|
||||
public class PermissionService(IPermissionRepository repo) : IPermissionService
|
||||
{
|
||||
public Task<bool> HasPermissionAsync(Guid roleId, string permissionCode) =>
|
||||
repo.HasPermissionAsync(roleId, permissionCode);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace Distribution.Infra.Authorization;
|
||||
|
||||
public class PermissionAuthorizationHandler : AuthorizationHandler<PermissionRequirement>
|
||||
{
|
||||
private readonly IPermissionService _permissionService;
|
||||
|
||||
public PermissionAuthorizationHandler(IPermissionService permissionService)
|
||||
{
|
||||
_permissionService = permissionService;
|
||||
}
|
||||
|
||||
protected override async Task HandleRequirementAsync(
|
||||
AuthorizationHandlerContext context, PermissionRequirement requirement)
|
||||
{
|
||||
var roleIdClaim = context.User.FindFirst("role_id")?.Value;
|
||||
if (!Guid.TryParse(roleIdClaim, out var roleId))
|
||||
return;
|
||||
|
||||
if (await _permissionService.HasPermissionAsync(roleId, requirement.PermissionCode))
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Distribution.Infra.Authorization;
|
||||
|
||||
public class PermissionPolicyProvider : IAuthorizationPolicyProvider
|
||||
{
|
||||
private readonly DefaultAuthorizationPolicyProvider _fallback;
|
||||
|
||||
public PermissionPolicyProvider(IOptions<AuthorizationOptions> options)
|
||||
{
|
||||
_fallback = new DefaultAuthorizationPolicyProvider(options);
|
||||
}
|
||||
|
||||
public Task<AuthorizationPolicy> GetDefaultPolicyAsync() => _fallback.GetDefaultPolicyAsync();
|
||||
|
||||
public Task<AuthorizationPolicy?> GetFallbackPolicyAsync() => _fallback.GetFallbackPolicyAsync();
|
||||
|
||||
public Task<AuthorizationPolicy?> GetPolicyAsync(string policyName)
|
||||
{
|
||||
if (policyName.Contains('.'))
|
||||
{
|
||||
var policy = new AuthorizationPolicyBuilder()
|
||||
.RequireAuthenticatedUser()
|
||||
.AddRequirements(new PermissionRequirement(policyName))
|
||||
.Build();
|
||||
return Task.FromResult<AuthorizationPolicy?>(policy);
|
||||
}
|
||||
|
||||
return _fallback.GetPolicyAsync(policyName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace Distribution.Infra.Authorization;
|
||||
|
||||
public class PermissionRequirement : IAuthorizationRequirement
|
||||
{
|
||||
public string PermissionCode { get; }
|
||||
|
||||
public PermissionRequirement(string permissionCode)
|
||||
{
|
||||
PermissionCode = permissionCode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Distribution.Infra.Authorization;
|
||||
|
||||
public interface IScopeAccessService
|
||||
{
|
||||
Task<bool> HasStoreScopeAsync(Guid userId, Guid storeId);
|
||||
Task<bool> HasRouteScopeAsync(Guid userId, Guid routeId);
|
||||
Task<bool> HasRegionScopeAsync(Guid userId, Guid regionId);
|
||||
Task<List<Guid>> GetAccessibleStoreIdsAsync(Guid userId);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Distribution.Infra.Authorization;
|
||||
|
||||
public interface IScopeRepository
|
||||
{
|
||||
Task<bool> HasStoreScopeAsync(Guid userId, Guid storeId);
|
||||
Task<bool> HasRouteScopeAsync(Guid userId, Guid routeId);
|
||||
Task<bool> HasRegionScopeAsync(Guid userId, Guid regionId);
|
||||
Task<List<Guid>> GetDirectStoreIdsAsync(Guid userId);
|
||||
Task<List<Guid>> GetRouteIdsAsync(Guid userId);
|
||||
Task<List<Guid>> GetRegionIdsAsync(Guid userId);
|
||||
Task<List<Guid>> GetStoreIdsByRouteIdsAsync(List<Guid> routeIds);
|
||||
Task<List<Guid>> GetRouteIdsByRegionIdsAsync(List<Guid> regionIds);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
namespace Distribution.Infra.Authorization;
|
||||
|
||||
public class ScopeAccessService(IScopeRepository repo) : IScopeAccessService
|
||||
{
|
||||
public Task<bool> HasStoreScopeAsync(Guid userId, Guid storeId) =>
|
||||
repo.HasStoreScopeAsync(userId, storeId);
|
||||
|
||||
public Task<bool> HasRouteScopeAsync(Guid userId, Guid routeId) =>
|
||||
repo.HasRouteScopeAsync(userId, routeId);
|
||||
|
||||
public Task<bool> HasRegionScopeAsync(Guid userId, Guid regionId) =>
|
||||
repo.HasRegionScopeAsync(userId, regionId);
|
||||
|
||||
public async Task<List<Guid>> GetAccessibleStoreIdsAsync(Guid userId)
|
||||
{
|
||||
var directStoreIds = await repo.GetDirectStoreIdsAsync(userId);
|
||||
var routeIds = await repo.GetRouteIdsAsync(userId);
|
||||
var regionIds = await repo.GetRegionIdsAsync(userId);
|
||||
|
||||
var storeIdsViaRoute = await repo.GetStoreIdsByRouteIdsAsync(routeIds);
|
||||
var routeIdsViaRegion = await repo.GetRouteIdsByRegionIdsAsync(regionIds);
|
||||
var storeIdsViaRegion = await repo.GetStoreIdsByRouteIdsAsync(routeIdsViaRegion);
|
||||
|
||||
return directStoreIds
|
||||
.Concat(storeIdsViaRoute)
|
||||
.Concat(storeIdsViaRegion)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using Distribution.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Distribution.Infra.Authorization;
|
||||
|
||||
public class ScopeRepository(AppDBContext context) : IScopeRepository
|
||||
{
|
||||
public async Task<bool> HasStoreScopeAsync(Guid userId, Guid storeId) =>
|
||||
await context.UserStoreScopes.AnyAsync(s => s.FK_UserId == userId && s.FK_StoreId == storeId && s.IsActive);
|
||||
|
||||
public async Task<bool> HasRouteScopeAsync(Guid userId, Guid routeId) =>
|
||||
await context.UserRouteScopes.AnyAsync(s => s.FK_UserId == userId && s.FK_RouteId == routeId && s.IsActive);
|
||||
|
||||
public async Task<bool> HasRegionScopeAsync(Guid userId, Guid regionId) =>
|
||||
await context.UserRegionScopes.AnyAsync(s => s.FK_UserId == userId && s.FK_RegionId == regionId && s.IsActive);
|
||||
|
||||
public async Task<List<Guid>> GetDirectStoreIdsAsync(Guid userId) =>
|
||||
await context.UserStoreScopes
|
||||
.Where(s => s.FK_UserId == userId && s.IsActive)
|
||||
.Select(s => s.FK_StoreId)
|
||||
.ToListAsync();
|
||||
|
||||
public async Task<List<Guid>> GetRouteIdsAsync(Guid userId) =>
|
||||
await context.UserRouteScopes
|
||||
.Where(s => s.FK_UserId == userId && s.IsActive)
|
||||
.Select(s => s.FK_RouteId)
|
||||
.ToListAsync();
|
||||
|
||||
public async Task<List<Guid>> GetRegionIdsAsync(Guid userId) =>
|
||||
await context.UserRegionScopes
|
||||
.Where(s => s.FK_UserId == userId && s.IsActive)
|
||||
.Select(s => s.FK_RegionId)
|
||||
.ToListAsync();
|
||||
|
||||
public async Task<List<Guid>> GetStoreIdsByRouteIdsAsync(List<Guid> routeIds) =>
|
||||
await context.Stores
|
||||
.Where(s => routeIds.Contains(s.FK_RouteId) && s.IsActive)
|
||||
.Select(s => s.Id)
|
||||
.ToListAsync();
|
||||
|
||||
public async Task<List<Guid>> GetRouteIdsByRegionIdsAsync(List<Guid> regionIds) =>
|
||||
await context.Routes
|
||||
.Where(r => regionIds.Contains(r.FK_RegionId) && r.IsActive)
|
||||
.Select(r => r.Id)
|
||||
.ToListAsync();
|
||||
}
|
||||
@@ -9,7 +9,7 @@ using Distribution.Features.Store;
|
||||
using Distribution.Features.User;
|
||||
using Distribution.Features.Vehicle;
|
||||
using Distribution.Features.Warehouse;
|
||||
using Distribution.Infra.Auth;
|
||||
using Distribution.Infra.Authorization;
|
||||
using Distribution.Infra.UoW;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
@@ -49,6 +49,8 @@ builder.Services.AddScoped<AuthService>();
|
||||
builder.Services.AddHttpContextAccessor();
|
||||
builder.Services.AddScoped<ICurrentUserService, CurrentUserService>();
|
||||
builder.Services.AddScoped<IJwtTokenService, JwtTokenService>();
|
||||
builder.Services.AddScoped<IPermissionRepository, PermissionRepository>();
|
||||
builder.Services.AddScoped<IScopeRepository, ScopeRepository>();
|
||||
builder.Services.AddScoped<IPermissionService, PermissionService>();
|
||||
builder.Services.AddScoped<IScopeAccessService, ScopeAccessService>();
|
||||
builder.Services.AddScoped<IAuthorizationHandler, PermissionAuthorizationHandler>();
|
||||
|
||||
Reference in New Issue
Block a user