feat: Implement user permission checks and manual payment functionalities
- Added CheckUserPermissionQuery and CheckUserPermissionQueryHandler for permission validation. - Introduced GetUserRolesQuery and GetUserRolesQueryHandler to retrieve user roles. - Created IPermissionService interface and its implementation in PermissionService. - Defined permission and role constants in PermissionDefinitions. - Developed SetDefaultVatPercentageCommand and its handler for VAT configuration. - Implemented GetCurrentVatPercentageQuery and handler to fetch current VAT settings. - Added manual payment commands: CreateManualPayment, ApproveManualPayment, and RejectManualPayment with respective handlers and validators. - Created GetManualPaymentsQuery and handler for retrieving manual payment records. - Integrated gRPC services for manual payments with appropriate permission checks. - Established Protobuf definitions for manual payment operations and metadata.
This commit is contained in:
@@ -31,5 +31,6 @@
|
||||
<ProjectReference Include="..\Protobufs\BackOffice.BFF.Role.Protobuf\BackOffice.BFF.Role.Protobuf.csproj" />
|
||||
<ProjectReference Include="..\Protobufs\BackOffice.BFF.UserRole.Protobuf\BackOffice.BFF.UserRole.Protobuf.csproj" />
|
||||
<ProjectReference Include="..\Protobufs\BackOffice.BFF.Category.Protobuf\BackOffice.BFF.Category.Protobuf.csproj" />
|
||||
<ProjectReference Include="..\Protobufs\BackOffice.BFF.ManualPayment.Protobuf\BackOffice.BFF.ManualPayment.Protobuf.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using BackOffice.BFF.Application.Common.Interfaces;
|
||||
using Grpc.AspNetCore.Server;
|
||||
using Grpc.Core;
|
||||
using Grpc.Core.Interceptors;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace BackOffice.BFF.WebApi.Common.Authorization;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
|
||||
public sealed class RequiresPermissionAttribute : Attribute
|
||||
{
|
||||
public RequiresPermissionAttribute(string permission)
|
||||
{
|
||||
Permission = permission ?? throw new ArgumentNullException(nameof(permission));
|
||||
}
|
||||
|
||||
public string Permission { get; }
|
||||
}
|
||||
|
||||
public class PermissionInterceptor : Interceptor
|
||||
{
|
||||
private readonly IPermissionService _permissionService;
|
||||
private readonly ILogger<PermissionInterceptor> _logger;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
public PermissionInterceptor(
|
||||
IPermissionService permissionService,
|
||||
ILogger<PermissionInterceptor> logger,
|
||||
IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
_permissionService = permissionService;
|
||||
_logger = logger;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
}
|
||||
|
||||
public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
|
||||
TRequest request,
|
||||
ServerCallContext context,
|
||||
UnaryServerMethod<TRequest, TResponse> continuation)
|
||||
{
|
||||
await EnsureHasPermissionAsync(context);
|
||||
return await continuation(request, context);
|
||||
}
|
||||
|
||||
private async Task EnsureHasPermissionAsync(ServerCallContext context)
|
||||
{
|
||||
var httpContext = context.GetHttpContext() ?? _httpContextAccessor.HttpContext;
|
||||
if (httpContext == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var endpoint = httpContext.GetEndpoint();
|
||||
if (endpoint == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var permissionAttributes = endpoint.Metadata.GetOrderedMetadata<RequiresPermissionAttribute>();
|
||||
if (permissionAttributes == null || permissionAttributes.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var attribute in permissionAttributes)
|
||||
{
|
||||
var hasPermission = await _permissionService.HasPermissionAsync(attribute.Permission, httpContext.RequestAborted);
|
||||
if (!hasPermission)
|
||||
{
|
||||
_logger.LogWarning("Permission denied for permission {Permission}", attribute.Permission);
|
||||
throw new RpcException(new Status(StatusCode.PermissionDenied, "Permission denied"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,3 +9,5 @@ global using Microsoft.AspNetCore.Builder;
|
||||
global using System;
|
||||
global using Microsoft.AspNetCore.Routing;
|
||||
global using System.Linq;
|
||||
global using BackOffice.BFF.WebApi.Common.Authorization;
|
||||
global using BackOffice.BFF.Application.Common.Models;
|
||||
|
||||
@@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Serilog;
|
||||
using Serilog.Core;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using BackOffice.BFF.WebApi.Common.Authorization;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
@@ -37,6 +38,7 @@ builder.Services.AddGrpc(options =>
|
||||
options.EnableDetailedErrors = true;
|
||||
options.MaxReceiveMessageSize = 1000 * 1024 * 1024; // 1 GB
|
||||
options.MaxSendMessageSize = 1000 * 1024 * 1024; // 1 GB
|
||||
options.Interceptors.Add<PermissionInterceptor>();
|
||||
}).AddJsonTranscoding();
|
||||
builder.Services.AddInfrastructureServices(builder.Configuration);
|
||||
builder.Services.AddApplicationServices();
|
||||
|
||||
@@ -21,6 +21,7 @@ public class ConfigurationService : ConfigurationContract.ConfigurationContractB
|
||||
_dispatchRequestToCQRS = dispatchRequestToCQRS;
|
||||
}
|
||||
|
||||
[RequiresPermission(PermissionNames.SettingsManageConfiguration)]
|
||||
public override async Task<Empty> CreateOrUpdateConfiguration(
|
||||
CreateOrUpdateConfigurationRequest request,
|
||||
ServerCallContext context)
|
||||
@@ -30,6 +31,7 @@ public class ConfigurationService : ConfigurationContract.ConfigurationContractB
|
||||
context);
|
||||
}
|
||||
|
||||
[RequiresPermission(PermissionNames.SettingsManageConfiguration)]
|
||||
public override async Task<Empty> DeactivateConfiguration(
|
||||
DeactivateConfigurationRequest request,
|
||||
ServerCallContext context)
|
||||
@@ -39,6 +41,7 @@ public class ConfigurationService : ConfigurationContract.ConfigurationContractB
|
||||
context);
|
||||
}
|
||||
|
||||
[RequiresPermission(PermissionNames.SettingsView)]
|
||||
public override async Task<GetAllConfigurationsResponse> GetAllConfigurations(
|
||||
GetAllConfigurationsRequest request,
|
||||
ServerCallContext context)
|
||||
|
||||
76
src/BackOffice.BFF.WebApi/Services/ManualPaymentService.cs
Normal file
76
src/BackOffice.BFF.WebApi/Services/ManualPaymentService.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using BackOffice.BFF.ManualPayment.Protobuf;
|
||||
using BackOffice.BFF.WebApi.Common.Services;
|
||||
using BackOffice.BFF.Application.ManualPaymentCQ.Commands.CreateManualPayment;
|
||||
using BackOffice.BFF.Application.ManualPaymentCQ.Commands.ApproveManualPayment;
|
||||
using BackOffice.BFF.Application.ManualPaymentCQ.Commands.RejectManualPayment;
|
||||
using BackOffice.BFF.Application.ManualPaymentCQ.Queries.GetManualPayments;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
using Mapster;
|
||||
using MediatR;
|
||||
|
||||
namespace BackOffice.BFF.WebApi.Services;
|
||||
|
||||
public class ManualPaymentService : ManualPaymentContract.ManualPaymentContractBase
|
||||
{
|
||||
private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS;
|
||||
private readonly ISender _sender;
|
||||
|
||||
public ManualPaymentService(
|
||||
IDispatchRequestToCQRS dispatchRequestToCQRS,
|
||||
ISender sender)
|
||||
{
|
||||
_dispatchRequestToCQRS = dispatchRequestToCQRS;
|
||||
_sender = sender;
|
||||
}
|
||||
|
||||
[RequiresPermission(PermissionNames.ManualPaymentsCreate)]
|
||||
public override async Task<CreateManualPaymentResponse> CreateManualPayment(
|
||||
CreateManualPaymentRequest request,
|
||||
ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<CreateManualPaymentRequest, CreateManualPaymentCommand, CreateManualPaymentResponse>(
|
||||
request,
|
||||
context);
|
||||
}
|
||||
|
||||
[RequiresPermission(PermissionNames.ManualPaymentsApprove)]
|
||||
public override async Task<Google.Protobuf.WellKnownTypes.Empty> ApproveManualPayment(
|
||||
ApproveManualPaymentRequest request,
|
||||
ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<ApproveManualPaymentRequest, ApproveManualPaymentCommand>(
|
||||
request,
|
||||
context);
|
||||
}
|
||||
|
||||
[RequiresPermission(PermissionNames.ManualPaymentsApprove)]
|
||||
public override async Task<Google.Protobuf.WellKnownTypes.Empty> RejectManualPayment(
|
||||
RejectManualPaymentRequest request,
|
||||
ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<RejectManualPaymentRequest, RejectManualPaymentCommand>(
|
||||
request,
|
||||
context);
|
||||
}
|
||||
|
||||
[RequiresPermission(PermissionNames.ManualPaymentsView)]
|
||||
public override async Task<GetManualPaymentsResponse> GetManualPayments(
|
||||
GetManualPaymentsRequest request,
|
||||
ServerCallContext context)
|
||||
{
|
||||
var query = new GetManualPaymentsQuery
|
||||
{
|
||||
PageNumber = request.PageNumber,
|
||||
PageSize = request.PageSize,
|
||||
UserId = request.UserId?.Value,
|
||||
Status = request.Status?.Value,
|
||||
Type = request.Type?.Value,
|
||||
ReferenceNumber = request.ReferenceNumber?.Value,
|
||||
// RequestedBy و OrderByDescending در این نسخه از UI ارسال نمیشود
|
||||
};
|
||||
|
||||
var result = await _sender.Send(query, context.CancellationToken);
|
||||
|
||||
return result.Adapt<GetManualPaymentsResponse>();
|
||||
}
|
||||
}
|
||||
@@ -19,47 +19,62 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase
|
||||
{
|
||||
_dispatchRequestToCQRS = dispatchRequestToCQRS;
|
||||
}
|
||||
|
||||
[RequiresPermission(PermissionNames.OrdersCreate)]
|
||||
public override async Task<CreateNewUserOrderResponse> CreateNewUserOrder(CreateNewUserOrderRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<CreateNewUserOrderRequest, CreateNewUserOrderCommand, CreateNewUserOrderResponse>(request, context);
|
||||
}
|
||||
|
||||
[RequiresPermission(PermissionNames.OrdersUpdate)]
|
||||
public override async Task<Empty> UpdateUserOrder(UpdateUserOrderRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<UpdateUserOrderRequest, UpdateUserOrderCommand>(request, context);
|
||||
}
|
||||
|
||||
[RequiresPermission(PermissionNames.OrdersDelete)]
|
||||
public override async Task<Empty> DeleteUserOrder(DeleteUserOrderRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<DeleteUserOrderRequest, DeleteUserOrderCommand>(request, context);
|
||||
}
|
||||
|
||||
[RequiresPermission(PermissionNames.OrdersView)]
|
||||
public override async Task<GetUserOrderResponse> GetUserOrder(GetUserOrderRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<GetUserOrderRequest, GetUserOrderQuery, GetUserOrderResponse>(request, context);
|
||||
}
|
||||
|
||||
[RequiresPermission(PermissionNames.OrdersView)]
|
||||
public override async Task<GetAllUserOrderByFilterResponse> GetAllUserOrderByFilter(GetAllUserOrderByFilterRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<GetAllUserOrderByFilterRequest, GetAllUserOrderByFilterQuery, GetAllUserOrderByFilterResponse>(request, context);
|
||||
}
|
||||
|
||||
[RequiresPermission(PermissionNames.OrdersUpdate)]
|
||||
public override async Task<UpdateOrderStatusResponse> UpdateOrderStatus(UpdateOrderStatusRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<UpdateOrderStatusRequest, UpdateOrderStatusCommand, UpdateOrderStatusResponse>(request, context);
|
||||
}
|
||||
|
||||
[RequiresPermission(PermissionNames.ReportsView)]
|
||||
public override async Task<GetOrdersByDateRangeResponse> GetOrdersByDateRange(GetOrdersByDateRangeRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<GetOrdersByDateRangeRequest, GetOrdersByDateRangeQuery, GetOrdersByDateRangeResponse>(request, context);
|
||||
}
|
||||
|
||||
[RequiresPermission(PermissionNames.OrdersUpdate)]
|
||||
public override async Task<ApplyDiscountToOrderResponse> ApplyDiscountToOrder(ApplyDiscountToOrderRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<ApplyDiscountToOrderRequest, ApplyDiscountToOrderCommand, ApplyDiscountToOrderResponse>(request, context);
|
||||
}
|
||||
|
||||
[RequiresPermission(PermissionNames.OrdersView)]
|
||||
public override async Task<CalculateOrderPVResponse> CalculateOrderPV(CalculateOrderPVRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<CalculateOrderPVRequest, CalculateOrderPVQuery, CalculateOrderPVResponse>(request, context);
|
||||
}
|
||||
|
||||
[RequiresPermission(PermissionNames.OrdersCancel)]
|
||||
public override async Task<CancelOrderResponse> CancelOrder(CancelOrderRequest request, ServerCallContext context)
|
||||
{
|
||||
return await _dispatchRequestToCQRS.Handle<CancelOrderRequest, CancelOrderCommand, CancelOrderResponse>(request, context);
|
||||
|
||||
Reference in New Issue
Block a user