using CleanArchitecture.Application.Common.Exceptions; using CleanArchitecture.Application.Common.Interfaces; using CleanArchitecture.Application.Common.Security; using MediatR; using System; using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; namespace CleanArchitecture.Application.Common.Behaviours { public class AuthorizationBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> { private readonly ICurrentUserService _currentUserService; private readonly IIdentityService _identityService; public AuthorizationBehaviour( ICurrentUserService currentUserService, IIdentityService identityService) { _currentUserService = currentUserService; _identityService = identityService; } public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next) { var authorizeAttributes = request.GetType().GetCustomAttributes<AuthorizeAttribute>(); if (authorizeAttributes.Any()) { // Must be authenticated user if (_currentUserService.UserId == null) { throw new UnauthorizedAccessException(); } // Role-based authorization var authorizeAttributesWithRoles = authorizeAttributes.Where(a => !string.IsNullOrWhiteSpace(a.Roles)); if (authorizeAttributesWithRoles.Any()) { foreach (var roles in authorizeAttributesWithRoles.Select(a => a.Roles.Split(','))) { var authorized = false; foreach (var role in roles) { var isInRole = await _identityService.IsInRoleAsync(_currentUserService.UserId, role.Trim()); if (isInRole) { authorized = true; break; } } // Must be a member of at least one role in roles if (!authorized) { throw new ForbiddenAccessException(); } } } // Policy-based authorization var authorizeAttributesWithPolicies = authorizeAttributes.Where(a => !string.IsNullOrWhiteSpace(a.Policy)); if (authorizeAttributesWithPolicies.Any()) { foreach(var policy in authorizeAttributesWithPolicies.Select(a => a.Policy)) { var authorized = await _identityService.AuthorizeAsync(_currentUserService.UserId, policy); if (!authorized) { throw new ForbiddenAccessException(); } } } } // User is authorized / authorization not required return await next(); } } }