Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/en/framework/infrastructure/audit-logging.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,9 @@ Configure<AbpAspNetCoreAuditingOptions>(options =>
});
````

`IgnoredUrls` is the only option. It is a list of ignored URLs prefixes. In the preceding example, all URLs starting with `/products` will be ignored for audit logging.
`IgnoredUrls` is a list of ignored URLs prefixes. In the preceding example, all URLs starting with `/products` will be ignored for audit logging.

`IgnoredHubTypes` is a list of ignored [SignalR](../real-time/signalr.md) hub types. The audited services called by these hubs are still audit logged.

## AbpAspNetCoreAuditingUrlOptions

Expand Down Expand Up @@ -185,8 +187,6 @@ The action is still audit logged and the parameter name is still written, but it

[Application service](../architecture/domain-driven-design/application-services.md) method calls also included into the audit log by default. You can use the `[DisableAuditing]` in service or method level.

> **Blazor Server limitation (Entity history):** In `Blazor Server` applications, entity change history is currently not guaranteed to be complete for every UI interaction. Blazor Server uses SignalR-based event handling, and under some flows the audit scope/action tracking may not align with `DbContext.SaveChanges`, which can cause missing or partial entity change records. This is a known platform-level limitation and not a regular configuration issue. See [#11682](https://github.com/abpframework/abp/issues/11682) for related discussions.

#### Enable/Disable for Other Services

Action audit logging can be enabled for any type of class (registered to and resolved from the [dependency injection](../fundamentals/dependency-injection.md)) while it is only enabled for the controllers and the application services by default.
Expand Down
13 changes: 13 additions & 0 deletions docs/en/framework/real-time/signalr.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,19 @@ Configure<AbpSignalROptions>(options =>
});
```

### Auditing

Use `AbpAspNetCoreAuditingOptions.IgnoredHubTypes` to ignore a hub (and the hubs derived from it) for [audit logging](../infrastructure/audit-logging.md):

```csharp
Configure<AbpAspNetCoreAuditingOptions>(options =>
{
options.IgnoredHubTypes.Add(typeof(MessagingHub));
});
```

The audited services called by an ignored hub are still audit logged. The hub of Blazor Server (and the interactive server render mode of Blazor Web App) is ignored by default.

### UserIdProvider

ABP implements SignalR's `IUserIdProvider` interface to provide the current user id from the `ICurrentUser` service of the ABP (see [the current user service](../infrastructure/current-user.md)), so it will be integrated to the authentication system of your application. The implementing class is the `AbpSignalRUserIdProvider`, if you want to change/override it.
Expand Down
2 changes: 0 additions & 2 deletions docs/en/modules/audit-logging-pro.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,6 @@ You can export audit logs to Excel by clicking the "Export to Excel" button in t

Entity changes tab is used to list, view and filter entity change logs.

> **Blazor Server note:** Entity change history can be missing or incomplete in some `Blazor Server` scenarios due to known SignalR/event-pipeline limitations. See [Audit Logging](../framework/infrastructure/audit-logging.md) and [#11682](https://github.com/abpframework/abp/issues/11682).

![audit-logging-module-entity-changes-list-page](../images/audit-logging-module-entity-changes-list-page.png)


Expand Down
1 change: 1 addition & 0 deletions framework/Volo.Abp.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@
<Project Path="test/Volo.Abp.AspNetCore.Mvc.PlugIn/Volo.Abp.AspNetCore.Mvc.PlugIn.csproj" />
<Project Path="test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj" />
<Project Path="test/Volo.Abp.AspNetCore.Mvc.UI.Tests/Volo.Abp.AspNetCore.Mvc.UI.Tests.csproj" />
<Project Path="test/Volo.Abp.AspNetCore.Components.Server.Tests/Volo.Abp.AspNetCore.Components.Server.Tests.csproj" />
<Project Path="test/Volo.Abp.AspNetCore.Components.Web.Theming.Tests/Volo.Abp.AspNetCore.Components.Web.Theming.Tests.csproj" />
<Project Path="test/Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Tests/Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Tests.csproj" />
<Project Path="test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests.csproj" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Net;
using System.Net.Http;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Components.Server;
using Microsoft.AspNetCore.Http.Connections;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -52,6 +53,14 @@ public override void ConfigureServices(ServiceConfigurationContext context)
Configure<AbpAspNetCoreAuditingOptions>(options =>
{
options.IgnoredUrls.AddIfNotContains("/_blazor");

// ComponentHub returns before component event handlers complete, so a hub level audit scope
// would be disposed before the application services add their actions and entity changes to it.
var componentHubType = typeof(CircuitOptions).Assembly.GetType("Microsoft.AspNetCore.Components.Server.ComponentHub");
if (componentHubType != null)
{
options.IgnoredHubTypes.AddIfNotContains(componentHubType);
}
});

if (!context.Services.ExecutePreConfiguredActions<AbpAspNetCoreComponentsWebOptions>().IsBlazorWebApp)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Auditing;
using Volo.Abp.Auditing;
using Volo.Abp.Uow;
using Volo.Abp.Users;
Expand All @@ -17,7 +18,7 @@ public class AbpAuditHubFilter : IHubFilter
public virtual async ValueTask<object?> InvokeMethodAsync(HubInvocationContext invocationContext, Func<HubInvocationContext, ValueTask<object?>> next)
{
var options = invocationContext.ServiceProvider.GetRequiredService<IOptions<AbpAuditingOptions>>().Value;
if (!options.IsEnabled)
if (!options.IsEnabled || IsIgnoredHub(invocationContext))
{
return await next(invocationContext);
}
Expand Down Expand Up @@ -76,6 +77,13 @@ public class AbpAuditHubFilter : IHubFilter
}
}

protected virtual bool IsIgnoredHub(HubInvocationContext invocationContext)
{
var options = invocationContext.ServiceProvider.GetRequiredService<IOptions<AbpAspNetCoreAuditingOptions>>().Value;
var hubType = invocationContext.Hub.GetType();
return options.IgnoredHubTypes.Any(ignoredHubType => ignoredHubType.IsAssignableFrom(hubType));
}

private async Task<bool> ShouldWriteAuditLogAsync(AuditLogInfo auditLogInfo, IServiceProvider serviceProvider, bool hasError)
{
var options = serviceProvider.GetRequiredService<IOptions<AbpAuditingOptions>>().Value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ public override void PreContribute(AuditLogContributionContext context)
return;
}

// The HTTP method of a non WebSocket transport (like the GET request of Server-Sent Events) belongs to the
// connection, not to the hub method call. Otherwise, the audit logs of the hub method calls are treated as GET requests.
context.AuditInfo.HttpMethod = null;

var clientInfoProvider = context.ServiceProvider.GetRequiredService<IWebClientInfoProvider>();
if (context.AuditInfo.ClientIpAddress == null)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;

namespace Volo.Abp.AspNetCore.Auditing;

Expand All @@ -11,4 +12,11 @@ public class AbpAspNetCoreAuditingOptions
/// starting with an ignored URL.
/// </summary>
public List<string> IgnoredUrls { get; } = new();

/// <summary>
/// SignalR hub types (including the derived types) that no audit log scope is created for,
/// neither for their requests by the <see cref="AbpAuditingMiddleware"/> nor for their method calls by the ABP SignalR integration.
/// The audited services called by these hubs still create and save their own audit logs.
/// </summary>
public List<Type> IgnoredHubTypes { get; } = new();
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Middleware;
using Volo.Abp.Auditing;
Expand Down Expand Up @@ -94,6 +95,17 @@ public async override Task InvokeAsync(HttpContext context, RequestDelegate next
}
}

protected async override Task<bool> ShouldSkipAsync(HttpContext context, RequestDelegate next)
{
var hubType = context.GetEndpoint()?.Metadata?.GetMetadata<HubMetadata>()?.HubType;
if (hubType != null && AspNetCoreAuditingOptions.IgnoredHubTypes.Any(ignoredHubType => ignoredHubType.IsAssignableFrom(hubType)))
{
return true;
}

return await base.ShouldSkipAsync(context, next);
}

private bool IsIgnoredUrl(HttpContext context)
{
if (context.Request.Path.Value == null)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<Import Project="..\..\..\common.test.props" />

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<RootNamespace />
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.AspNetCore.Components.Server\Volo.Abp.AspNetCore.Components.Server.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System.Linq;
using Microsoft.Extensions.Options;
using Shouldly;
using Volo.Abp.AspNetCore.Auditing;
using Volo.Abp.Modularity;
using Volo.Abp.Testing;
using Xunit;

namespace Volo.Abp.AspNetCore.Components.Server;

public abstract class AbpAspNetCoreComponentsServerModule_Tests<TStartupModule> : AbpIntegratedTest<TStartupModule>
where TStartupModule : IAbpModule
{
private const string ComponentHubTypeFullName = "Microsoft.AspNetCore.Components.Server.ComponentHub";

private readonly AbpAspNetCoreAuditingOptions _auditingOptions;

protected AbpAspNetCoreComponentsServerModule_Tests()
{
_auditingOptions = GetRequiredService<IOptions<AbpAspNetCoreAuditingOptions>>().Value;
}

[Fact]
public void Should_Only_Ignore_ComponentHub_For_Hub_Auditing()
{
_auditingOptions.IgnoredHubTypes.Single().FullName.ShouldBe(ComponentHubTypeFullName);
}

[Fact]
public void Should_Ignore_Blazor_Urls_For_Auditing()
{
_auditingOptions.IgnoredUrls.ShouldContain("/_blazor");
}
}

public class AbpAspNetCoreComponentsServerModule_BlazorServer_Tests : AbpAspNetCoreComponentsServerModule_Tests<AbpAspNetCoreComponentsServerTestModule>
{
}

public class AbpAspNetCoreComponentsServerModule_BlazorWebApp_Tests : AbpAspNetCoreComponentsServerModule_Tests<AbpAspNetCoreComponentsServerWebAppTestModule>
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Volo.Abp.Modularity;

namespace Volo.Abp.AspNetCore.Components.Server;

[DependsOn(typeof(AbpAspNetCoreComponentsServerModule))]
public class AbpAspNetCoreComponentsServerTestModule : AbpModule
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Volo.Abp.AspNetCore.Components.Web;
using Volo.Abp.Modularity;

namespace Volo.Abp.AspNetCore.Components.Server;

[DependsOn(typeof(AbpAspNetCoreComponentsServerModule))]
public class AbpAspNetCoreComponentsServerWebAppTestModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpAspNetCoreComponentsWebOptions>(options =>
{
options.IsBlazorWebApp = true;
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Volo.Abp.AspNetCore.SignalR\Volo.Abp.AspNetCore.SignalR.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.EntityFrameworkCore.Sqlite\Volo.Abp.EntityFrameworkCore.Sqlite.csproj" />
<ProjectReference Include="..\Volo.Abp.AspNetCore.Tests\Volo.Abp.AspNetCore.Tests.csproj" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Auditing;
using Volo.Abp.AspNetCore.SignalR.Auditing.TestApp;
using Volo.Abp.AspNetCore.SignalR.SampleHubs;
using Volo.Abp.AspNetCore.TestBase;
using Volo.Abp.Auditing;
using Volo.Abp.Autofac;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.Sqlite;
using Volo.Abp.Modularity;
using Volo.Abp.Uow;

Expand All @@ -12,24 +19,75 @@ namespace Volo.Abp.AspNetCore.SignalR;
[DependsOn(
typeof(AbpAspNetCoreSignalRModule),
typeof(AbpAspNetCoreTestBaseModule),
typeof(AbpAutofacModule)
typeof(AbpAutofacModule),
typeof(AbpEntityFrameworkCoreSqliteModule)
)]
public class AbpAspNetCoreSignalRTestModule : AbpModule
{
public const string UnitOfWorkReservedHeaderName = "X-Unit-Of-Work-Reserved";

public const string AuditScopeStartedHeaderName = "X-Audit-Scope-Started";

public static Exception UseConfiguredEndpointsException { get; set; }

private AbpUnitTestSqliteDatabase _database;

public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAbpDbContext<SignalRAuditingTestDbContext>(options =>
{
options.AddDefaultRepositories(includeAllEntities: true);
});

_database = new AbpUnitTestSqliteDatabase();
_database.CreateTables(new SignalRAuditingTestDbContext(
new DbContextOptionsBuilder<SignalRAuditingTestDbContext>()
.UseSqlite(_database.ConnectionString)
.AddAbpDbContextOptionsExtension()
.Options));

Configure<AbpDbConnectionOptions>(options =>
{
options.ConnectionStrings.Default = _database.ConnectionString;
});

Configure<AbpDbContextOptions>(options =>
{
options.Configure(configurationContext =>
{
configurationContext.UseSqlite();
});
});

Configure<AbpAspNetCoreAuditingOptions>(options =>
{
options.IgnoredHubTypes.Add(typeof(IgnoredAuditingHub));
});

Configure<AbpAuditingOptions>(options =>
{
options.EntityHistorySelectors.Add(new NamedTypeSelector(nameof(AuditedNote), type => type == typeof(AuditedNote)));
});
}

public override void OnApplicationShutdown(ApplicationShutdownContext context)
{
_database?.Dispose();
}

public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();

app.UseRouting();
app.UseUnitOfWork();
app.UseAuditing();
app.Use(async (httpContext, next) =>
{
httpContext.Response.Headers[UnitOfWorkReservedHeaderName] =
(httpContext.RequestServices.GetRequiredService<IAmbientUnitOfWork>().UnitOfWork?.IsReserved == true).ToString();
httpContext.Response.Headers[AuditScopeStartedHeaderName] =
(httpContext.RequestServices.GetRequiredService<IAuditingManager>().Current != null).ToString();

await next();
});
Expand All @@ -40,6 +98,7 @@ public override void OnApplicationInitialization(ApplicationInitializationContex
app.UseConfiguredEndpoints(endpoints =>
{
endpoints.MapHub<DisableAutoHubMapHub>("/manually-mapped-hub");
endpoints.MapHub<IgnoredAuditingHub>("/ignored-auditing-hub");
});
}
catch (Exception e)
Expand Down
Loading
Loading