From 8e84e582337e0ccd0de3063362ee35c52f621d67 Mon Sep 17 00:00:00 2001 From: maliming Date: Tue, 9 Nov 2021 16:04:32 +0800 Subject: [PATCH 1/2] Fix audit issues in Blazor Server. Resolve #10527 --- .../SignalR/AbpAspNetCoreSignalRModule.cs | 12 ++- .../Abp/AspNetCore/SignalR/AbpHubContext.cs | 26 ++++++ .../SignalR/AbpHubContextAccessorHubFilter.cs | 23 +++++ .../SignalR/Auditing/AbpAuditHubFilter.cs | 91 +++++++++++++++++++ .../AspNetCoreSignalRAuditLogContributor.cs | 55 +++++++++++ .../AbpAuthenticationHubFilter.cs} | 4 +- .../SignalR/DefaultAbpHubContextAccessor.cs | 23 +++++ .../SignalR/IAbpHubContextAccessor.cs | 12 +++ .../Auditing/AspNetCoreAuditLogContributor.cs | 5 + 9 files changed, 248 insertions(+), 3 deletions(-) create mode 100644 framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpHubContext.cs create mode 100644 framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpHubContextAccessorHubFilter.cs create mode 100644 framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/Auditing/AbpAuditHubFilter.cs create mode 100644 framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/Auditing/AspNetCoreSignalRAuditLogContributor.cs rename framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/{AbpSignalRHubFilter.cs => Authentication/AbpAuthenticationHubFilter.cs} (93%) create mode 100644 framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/DefaultAbpHubContextAccessor.cs create mode 100644 framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/IAbpHubContextAccessor.cs diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRModule.cs b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRModule.cs index 767f81b452c..a0b57db54d8 100644 --- a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRModule.cs @@ -8,6 +8,9 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Volo.Abp.AspNetCore.Auditing; +using Volo.Abp.AspNetCore.SignalR.Auditing; +using Volo.Abp.AspNetCore.SignalR.Authentication; +using Volo.Abp.Auditing; using Volo.Abp.DependencyInjection; using Volo.Abp.Modularity; @@ -34,7 +37,9 @@ public override void ConfigureServices(ServiceConfigurationContext context) var routePatterns = new List {"/signalr-hubs"}; var signalRServerBuilder = context.Services.AddSignalR(options => { - options.AddFilter(); + options.AddFilter(); + options.AddFilter(); + options.AddFilter(); }); context.Services.ExecutePreConfiguredActions(signalRServerBuilder); @@ -75,6 +80,11 @@ public override void ConfigureServices(ServiceConfigurationContext context) options.IgnoredUrls.AddIfNotContains(x => routePattern.StartsWith(x), () => routePattern); } }); + + Configure(options => + { + options.Contributors.Add(new AspNetCoreSignalRAuditLogContributor()); + }); } private void AutoAddHubTypes(IServiceCollection services) diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpHubContext.cs b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpHubContext.cs new file mode 100644 index 00000000000..29308feeb85 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpHubContext.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using Microsoft.AspNetCore.SignalR; + +namespace Volo.Abp.AspNetCore.SignalR +{ + public class AbpHubContext + { + public IServiceProvider ServiceProvider { get; } + + public Hub Hub { get; } + + public MethodInfo HubMethod { get; } + + public IReadOnlyList HubMethodArguments { get; } + + public AbpHubContext(IServiceProvider serviceProvider, Hub hub, MethodInfo hubMethod, IReadOnlyList hubMethodArguments) + { + ServiceProvider = serviceProvider; + Hub = hub; + HubMethod = hubMethod; + HubMethodArguments = hubMethodArguments; + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpHubContextAccessorHubFilter.cs b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpHubContextAccessorHubFilter.cs new file mode 100644 index 00000000000..8a68a8ebbc4 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpHubContextAccessorHubFilter.cs @@ -0,0 +1,23 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.SignalR; +using Microsoft.Extensions.DependencyInjection; + +namespace Volo.Abp.AspNetCore.SignalR +{ + public class AbpHubContextAccessorHubFilter : IHubFilter + { + public virtual async ValueTask InvokeMethodAsync(HubInvocationContext invocationContext, Func> next) + { + var hubContextAccessor = invocationContext.ServiceProvider.GetRequiredService(); + using (hubContextAccessor.Change(new AbpHubContext( + invocationContext.ServiceProvider, + invocationContext.Hub, + invocationContext.HubMethod, + invocationContext.HubMethodArguments))) + { + return await next(invocationContext); + } + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/Auditing/AbpAuditHubFilter.cs b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/Auditing/AbpAuditHubFilter.cs new file mode 100644 index 00000000000..a04cf6abcd3 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/Auditing/AbpAuditHubFilter.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.SignalR; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Volo.Abp.Auditing; +using Volo.Abp.Uow; +using Volo.Abp.Users; + +namespace Volo.Abp.AspNetCore.SignalR.Auditing +{ + public class AbpAuditHubFilter : IHubFilter + { + public virtual async ValueTask InvokeMethodAsync(HubInvocationContext invocationContext, Func> next) + { + var options = invocationContext.ServiceProvider.GetRequiredService>().Value; + if (!options.IsEnabled) + { + return await next(invocationContext); + } + + var hasError = false; + var auditingManager = invocationContext.ServiceProvider.GetRequiredService(); + using (var saveHandle = auditingManager.BeginScope()) + { + Debug.Assert(auditingManager.Current != null); + object result; + try + { + result = await next(invocationContext); + + if (auditingManager.Current.Log.Exceptions.Any()) + { + hasError = true; + } + } + catch (Exception ex) + { + hasError = true; + + if (!auditingManager.Current.Log.Exceptions.Contains(ex)) + { + auditingManager.Current.Log.Exceptions.Add(ex); + } + + throw; + } + finally + { + if (ShouldWriteAuditLog(invocationContext.ServiceProvider, hasError)) + { + var unitOfWorkManager = invocationContext.ServiceProvider.GetRequiredService(); + if (unitOfWorkManager.Current != null) + { + await unitOfWorkManager.Current.SaveChangesAsync(); + } + + await saveHandle.SaveAsync(); + } + } + + return result; + } + } + + private bool ShouldWriteAuditLog(IServiceProvider serviceProvider, bool hasError) + { + var options = serviceProvider.GetRequiredService>().Value; + if (options.AlwaysLogOnException && hasError) + { + return true; + } + + if (!options.IsEnabledForAnonymousUsers && !serviceProvider.GetRequiredService().IsAuthenticated) + { + return false; + } + + var auditingManager = serviceProvider.GetRequiredService(); + if (auditingManager.Current != null && auditingManager.Current.Log.Actions.IsNullOrEmpty()) + { + return false; + } + + return true; + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/Auditing/AspNetCoreSignalRAuditLogContributor.cs b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/Auditing/AspNetCoreSignalRAuditLogContributor.cs new file mode 100644 index 00000000000..7b20bc05860 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/Auditing/AspNetCoreSignalRAuditLogContributor.cs @@ -0,0 +1,55 @@ +using System.Linq; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Volo.Abp.AspNetCore.WebClientInfo; +using Volo.Abp.Auditing; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.AspNetCore.SignalR.Auditing +{ + public class AspNetCoreSignalRAuditLogContributor : AuditLogContributor, ITransientDependency + { + public ILogger Logger { get; set; } + + public AspNetCoreSignalRAuditLogContributor() + { + Logger = NullLogger.Instance; + } + + public override void PreContribute(AuditLogContributionContext context) + { + var hubContext = context.ServiceProvider.GetRequiredService().Context; + if (hubContext == null) + { + return; + } + + var clientInfoProvider = context.ServiceProvider.GetRequiredService(); + if (context.AuditInfo.ClientIpAddress == null) + { + context.AuditInfo.ClientIpAddress = clientInfoProvider.ClientIpAddress; + } + + if (context.AuditInfo.BrowserInfo == null) + { + context.AuditInfo.BrowserInfo = clientInfoProvider.BrowserInfo; + } + + //TODO: context.AuditInfo.ClientName + } + + public override void PostContribute(AuditLogContributionContext context) + { + var hubContext = context.ServiceProvider.GetRequiredService().Context; + if (hubContext == null) + { + return; + } + + var firstAction = context.AuditInfo.Actions.FirstOrDefault(); + context.AuditInfo.Url = firstAction?.ServiceName + "." + firstAction?.MethodName; + context.AuditInfo.HttpStatusCode = null; + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpSignalRHubFilter.cs b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/Authentication/AbpAuthenticationHubFilter.cs similarity index 93% rename from framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpSignalRHubFilter.cs rename to framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/Authentication/AbpAuthenticationHubFilter.cs index 26956293048..eed252bfe6a 100644 --- a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpSignalRHubFilter.cs +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/Authentication/AbpAuthenticationHubFilter.cs @@ -4,9 +4,9 @@ using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Security.Claims; -namespace Volo.Abp.AspNetCore.SignalR +namespace Volo.Abp.AspNetCore.SignalR.Authentication { - public class AbpSignalRHubFilter : IHubFilter + public class AbpAuthenticationHubFilter : IHubFilter { public virtual async ValueTask InvokeMethodAsync(HubInvocationContext invocationContext, Func> next) { diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/DefaultAbpHubContextAccessor.cs b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/DefaultAbpHubContextAccessor.cs new file mode 100644 index 00000000000..6ef0c02d828 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/DefaultAbpHubContextAccessor.cs @@ -0,0 +1,23 @@ +using System; +using System.Threading; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.AspNetCore.SignalR +{ + public class DefaultAbpHubContextAccessor : IAbpHubContextAccessor, ISingletonDependency + { + public AbpHubContext Context => _currentHubContext.Value; + + private readonly AsyncLocal _currentHubContext = new AsyncLocal(); + + public virtual IDisposable Change(AbpHubContext context) + { + var parent = Context; + _currentHubContext.Value = context; + return new DisposeAction(() => + { + _currentHubContext.Value = parent; + }); + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/IAbpHubContextAccessor.cs b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/IAbpHubContextAccessor.cs new file mode 100644 index 00000000000..052c6a9be1a --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/IAbpHubContextAccessor.cs @@ -0,0 +1,12 @@ +using System; + +namespace Volo.Abp.AspNetCore.SignalR +{ + public interface IAbpHubContextAccessor + { + AbpHubContext Context { get; } + + IDisposable Change(AbpHubContext context); + } +} + diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Auditing/AspNetCoreAuditLogContributor.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Auditing/AspNetCoreAuditLogContributor.cs index 04238a9a45e..2d3fd11705e 100644 --- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Auditing/AspNetCoreAuditLogContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Auditing/AspNetCoreAuditLogContributor.cs @@ -26,6 +26,11 @@ public override void PreContribute(AuditLogContributionContext context) return; } + if (httpContext.WebSockets.IsWebSocketRequest) + { + return; + } + if (context.AuditInfo.HttpMethod == null) { context.AuditInfo.HttpMethod = httpContext.Request.Method; From c0206bafcf28d3cfac58ba6e088cb716c1ed48a1 Mon Sep 17 00:00:00 2001 From: maliming Date: Tue, 9 Nov 2021 16:08:44 +0800 Subject: [PATCH 2/2] Update AbpAuditHubFilter.cs --- .../Volo/Abp/AspNetCore/SignalR/Auditing/AbpAuditHubFilter.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/Auditing/AbpAuditHubFilter.cs b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/Auditing/AbpAuditHubFilter.cs index a04cf6abcd3..ec51b93bf61 100644 --- a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/Auditing/AbpAuditHubFilter.cs +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/Auditing/AbpAuditHubFilter.cs @@ -80,7 +80,8 @@ private bool ShouldWriteAuditLog(IServiceProvider serviceProvider, bool hasError } var auditingManager = serviceProvider.GetRequiredService(); - if (auditingManager.Current != null && auditingManager.Current.Log.Actions.IsNullOrEmpty()) + if (auditingManager.Current == null || + auditingManager.Current.Log.Actions.IsNullOrEmpty()) { return false; }