Unfortunately, I cannot share the source code, but I'll try to describe what I've found out.
I've revealed that the issue happens when I am trying to modify HttpContext
response stream. I have to return custom JSON response when my application throws an exception. For this goal, the middleware has been created. It analyzes HttpContext
response headers and replaces this response stream - when an exception happens - with custom JSON response. The middleware uses IAuditingManager
to collect the information about errors.
Do you have any idea how modifying HttpContext
response stream may affect IAuditLogRepository
logic?
This issue can be reproduced when you are trying to save some error (the exception which happens in your application). Have you reproduced this case?
Hi. Not exactly.
I use the RequiredPolicy
options for routing configuration of an application menu. It allows me to limit the displayed menu items based on user permissions. When a user tries to navigate to the page directly - he sees 403 error page. I would like to show information about a missing permission here. But HttpErrorResponse
object doesn't contain such information. I've made the research of JS code and found out that the inner logic doesn't include it for HttpErrorResponse
object. Please see the corresponding part of your source code below:
First of all, the "MaxExceptionsLengthValue" property is missed in "Volo.Abp.AuditLogging.Domain.Shared" package version "5.1.2". Besides, you suggest me to make changes inside ABP module. Also, I've asked, "How exception can be truncated"? I don't want to change the length of DB field.
Hi.
I've read the thread which you advice. But this solution looks like a trick. Would be great if the logic was contained inside "Volo.Abp.AuditLogging" and there was no need to make external solution modifications. We use ABP framework for developing a lot of services. What will happen, when ABP team changes the logic and structure of AuditLog tables in some new release?
We would prefer to truncate an error message for AuditLog instead of changing the corresponding DB field size. What is a proper way of doing this?
Hi. Here is the reproducable situation in the test project attached. Let's say I'm trying to access http://localhost:4200/dashboard via typing the address in a browser and the logged user does not have access to this page. IAuthorizationMiddlewareResultHandler
implementation is even not triggered - straight away default 403 forbidden is shown.
P.S. Could you please add possibility to attach code zip archives to a message? It's not user-friendly to create cloud links each time.
Ah, OK. What about ABP Permission Grant Cache? Should I use Redis to share one cache between two apps (hosted as separate IIS apps eventually) (or my approach - exchanging messages via bus and refreshing the cache in dependent apps?)
Please specify where I broke ABP design: both applications 1 and applications 2 - are ABP-framework-based. Permissions are set in application 1, since it's a "main" application. The issue was that the permissions set in application 1 were not applied to application 2. I found "workaround", but my question in the first place would be: "Why they are not applied to application 2? They are not applied even if a distributed cache key is not set in both applications. Is it because the cache in both applications is separate? Is using Redis cache a way to go then?"
I understood my mistake in the code: I am missing setting current tenant before changing cache. I am going to send tenant GUID from RabbitMq sender.
Anyway, here's the override of PermissionAppService
in back-end 1:
using Volo.Abp.PermissionManagement;
using System.Threading.Tasks;
using Volo.Abp.Authorization.Permissions;
using Microsoft.Extensions.Options;
using System.Linq;
using Volo.Abp.DependencyInjection;
using AbxEps.RabbitMq.Client;
using AbxEps.RabbitMq.Client.Messages;
using AbxEps.Fines;
namespace AbxEps.CentralTools
{
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IPermissionAppService))]
public class CentralToolsPermissionAppService : PermissionAppService
{
private readonly IRabbitMqManager _rabbitMqManager;
public CentralToolsPermissionAppService(
IPermissionManager permissionManager,
IPermissionDefinitionManager permissionDefinitionManager,
IOptions<PermissionManagementOptions> options,
IPermissionStateManager permissionStateManager,
IRabbitMqManager rabbitMqManager)
: base(permissionManager, permissionDefinitionManager, options, permissionStateManager)
{
_rabbitMqManager = rabbitMqManager;
}
public override async Task UpdateAsync(string providerName, string providerKey, UpdatePermissionsDto input)
{
await base.UpdateAsync(providerName, providerKey, input);
var cacheRabbitMqInput = new CacheRabbitMqInput(input.Permissions.Select(x => x)
.ToDictionary(x => PermissionGrantCacheItem.CalculateCacheKey(x.Name, providerName, providerKey), x => x.IsGranted));
await _rabbitMqManager.CreateSender().SendAsync(new RabbitMqMessage<CacheRabbitMqInput>
{
RoutingKey = "AbxEps-Abp-Caching",
Body = cacheRabbitMqInput
});
}
}
}
Here's the triggered RabbitMq subscription in back-end 2:
using Volo.Abp.PermissionManagement;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Distributed;
using System.Threading.Tasks;
using Volo.Abp.Caching;
using System.Collections.Generic;
using AbxEps.RabbitMq.Client.Receivers;
using RabbitMQ.Client.Events;
using System;
using Microsoft.Extensions.DependencyInjection;
using AbxEps.RabbitMq.Client.Extensions;
using System.Linq;
namespace AbxEps.Fines
{
public class FinesRabbitMqReceiver : RabbitMqReceiverBase
{
private readonly IServiceProvider _serviceProvider;
public FinesRabbitMqReceiver(IServiceProvider serviceProvider) : base("AbxEps-Abp-Caching")
{
_serviceProvider = serviceProvider;
}
public override async Task<object> Received(BasicDeliverEventArgs @event)
{
using var scope = _serviceProvider.CreateScope();
var permissionGrantCache = scope.ServiceProvider.GetService<IDistributedCache<PermissionGrantCacheItem>>();
var permissions = @event.ToDataObject<Dictionary<string, bool>>();
await permissionGrantCache.SetManyAsync(
permissions.Select(permission =>
new KeyValuePair<string, PermissionGrantCacheItem>(permission.Key, new PermissionGrantCacheItem(permission.Value))));
return Task.FromResult<object>(null);
}
}
}
Does this approach to refresh permissions in another app looks OK or I'm missing a better ABP approach?
UPDATE: i've used RabbitMq to notify Angular 2 about permission changes in Angular 1. I am now trying to modify Angular 2 permission cache via SetManyAsync
- it adds the values to the cache in Angular 2, but it still does not reflect the changes on page refresh...