diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index f63ada7..1f3f73f 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -16,18 +16,18 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Setup .NET Core 3.1 - uses: actions/setup-dotnet@v1 + - name: Setup .NET 8 + uses: actions/setup-dotnet@v4 with: - dotnet-version: '3.1.x' - - name: Setup .NET 5.0 - uses: actions/setup-dotnet@v1 + dotnet-version: '8.x' + - name: Setup .NET 9 + uses: actions/setup-dotnet@v4 with: - dotnet-version: '5.0.x' - - name: Setup .NET 6.0 - uses: actions/setup-dotnet@v1 + dotnet-version: '9.x' + - name: Setup .NET 10 + uses: actions/setup-dotnet@v4 with: - dotnet-version: '6.0.x' + dotnet-version: '10.x' - name: Fix Windows VM bug shell: bash if: matrix.os == 'windows-latest' @@ -44,7 +44,7 @@ jobs: run: dotnet pack -c Release - name: Publish artifact if: matrix.os == 'ubuntu-latest' - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: nupkg path: '**/*.nupkg' diff --git a/NuGet.AzDO.config b/NuGet.AzDO.config index 9a058af..c9581ab 100644 --- a/NuGet.AzDO.config +++ b/NuGet.AzDO.config @@ -1,7 +1,7 @@  - + diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 6046c87..45c1301 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -7,11 +7,11 @@ variables: - name: buildConfiguration value: 'Release' - name: packageVersion - value: '2.0.0.0' + value: '3.0.0.0' - name: efPackageVersion - value: '2.0.0.0' -- name: identityPackageVersion value: '3.0.0.0' +- name: identityPackageVersion + value: '4.0.0.0' - name: RunSqlServerTest value: 'true' stages: @@ -40,22 +40,22 @@ stages: mkdir LocalNuget workingDirectory: $(Build.SourcesDirectory) - task: UseDotNet@2 - displayName: Install .NET Core sdk version 5.x + displayName: Install .NET Core sdk version 8.x inputs: packageType: sdk - version: 5.x + version: 8.x installationPath: $(Agent.ToolsDirectory)/dotnet - task: UseDotNet@2 - displayName: Install .NET Core sdk version 3.1.x + displayName: Install .NET Core sdk version 9.x inputs: packageType: sdk - version: 3.1.x + version: 9.x installationPath: $(Agent.ToolsDirectory)/dotnet - task: UseDotNet@2 - displayName: Install .NET Core sdk version 6.0.x + displayName: Install .NET Core sdk version 10.x inputs: packageType: sdk - version: 6.0.x + version: 10.x installationPath: $(Agent.ToolsDirectory)/dotnet - task: NuGetToolInstaller@0 inputs: diff --git a/src/RSK.IdentityServer4.AuditEventSink/RSK.IdentityServer4.AuditEventSink.csproj b/src/RSK.IdentityServer4.AuditEventSink/RSK.IdentityServer4.AuditEventSink.csproj index d20d010..b5e3581 100644 --- a/src/RSK.IdentityServer4.AuditEventSink/RSK.IdentityServer4.AuditEventSink.csproj +++ b/src/RSK.IdentityServer4.AuditEventSink/RSK.IdentityServer4.AuditEventSink.csproj @@ -1,22 +1,22 @@  - netcoreapp3.1 + net8.0;net9.0;net10.0 Rock Solid Knowledge Ltd IdentityServer4 event sink to add audit records into AdminUI auditing https://github.com/RockSolidKnowledge/RSK.IdentityServer4.AuditEventSink - Updated to support only ASP.NET Core 3.1 and fixed actor type for consent granted event. + Upgrade to .NET 10 Copyright 2021 (c) Rock Solid Knowledge Ltd. All rights reserved Audit AdminUI IdentityServer Events true icon.png Apache-2.0 - 1.1.1 + 2.0.0 - + diff --git a/src/Rsk.Audit.EF/AuditDatabaseContext.cs b/src/Rsk.Audit.EF/AuditDatabaseContext.cs index 57c0c10..a8176fe 100644 --- a/src/Rsk.Audit.EF/AuditDatabaseContext.cs +++ b/src/Rsk.Audit.EF/AuditDatabaseContext.cs @@ -7,15 +7,20 @@ namespace RSK.Audit.EF internal class AuditDatabseUnitOfWorkFactory : IUnitOfOWorkFactory { private readonly DbContextOptions options; + private readonly string dbSchema; - public AuditDatabseUnitOfWorkFactory(DbContextOptions options) + public AuditDatabseUnitOfWorkFactory(DbContextOptions options, string dbSchema = null) { this.options = options; - + this.dbSchema = dbSchema; } public IUnitOfWork Create() { + if (!string.IsNullOrEmpty(dbSchema)) + { + return new AuditDatabaseContext(options, dbSchema); + } return new AuditDatabaseContext(options); } } @@ -27,11 +32,23 @@ public AuditDatabaseContext(DbContextOptions options) : ba } + public AuditDatabaseContext(DbContextOptions options, string schema) : base(options) + { + Schema = schema; + } + + public string Schema { get; set; } + public DbSet AuditEntries { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { + if (!string.IsNullOrEmpty(Schema)) + { + modelBuilder.Model.SetOrRemoveAnnotation("Relational:DefaultSchema", Schema); + } + modelBuilder.Entity() .HasKey(ae => ae.Id); @@ -40,9 +57,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity() .Property(e => e.When); - - - + base.OnModelCreating(modelBuilder); } diff --git a/src/Rsk.Audit.EF/AuditProviderFactory.cs b/src/Rsk.Audit.EF/AuditProviderFactory.cs index 9005c3b..1911c41 100644 --- a/src/Rsk.Audit.EF/AuditProviderFactory.cs +++ b/src/Rsk.Audit.EF/AuditProviderFactory.cs @@ -5,22 +5,28 @@ namespace RSK.Audit.EF public class AuditProviderFactory : Audit.AuditProviderFactory { private readonly DbContextOptions options; + private readonly string dbSchema; - public AuditProviderFactory(DbContextOptions options) + public AuditProviderFactory(DbContextOptions options, string dbSchema = null) { this.options = options; + this.dbSchema = dbSchema; } public override IRecordAuditableActions CreateAuditSource(string source) { - var uwf = new AuditDatabseUnitOfWorkFactory(options); + var uwf = !string.IsNullOrWhiteSpace(dbSchema) ? + new AuditDatabseUnitOfWorkFactory(options, dbSchema) : + new AuditDatabseUnitOfWorkFactory(options); return new AuditRecorder(source,uwf); } public override IQueryableAuditableActions CreateAuditQuery() { - var uwf = new AuditDatabseUnitOfWorkFactory(options); + var uwf = !string.IsNullOrWhiteSpace(dbSchema) ? + new AuditDatabseUnitOfWorkFactory(options, dbSchema) : + new AuditDatabseUnitOfWorkFactory(options); return new AuditQueryFactory(uwf); } diff --git a/src/Rsk.Audit.EF/Rsk.Audit.EF.csproj b/src/Rsk.Audit.EF/Rsk.Audit.EF.csproj index c8cf925..2ab896f 100644 --- a/src/Rsk.Audit.EF/Rsk.Audit.EF.csproj +++ b/src/Rsk.Audit.EF/Rsk.Audit.EF.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1;net5.0;net6.0 + net8.0;net9.0;net10.0 RSK.Audit.EF Rock Solid Knowledge Ltd Provides audting API to record audit records and query api to find audit records with EntityFramework Core @@ -14,7 +14,7 @@ icon.png Apache-2.0 RSK.Audit.EF - 2.0.0 + 3.0.0 @@ -22,26 +22,22 @@ 1591 - - + + - - + + - - + + - + - - - - diff --git a/src/Rsk.Audit/Rsk.Audit.csproj b/src/Rsk.Audit/Rsk.Audit.csproj index ac1a824..b7596d2 100644 --- a/src/Rsk.Audit/Rsk.Audit.csproj +++ b/src/Rsk.Audit/Rsk.Audit.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1;net5.0;net6.0 + net8.0;net9.0;net10.0 RSK.Audit Rock Solid Knowledge Ltd RSK.Audit @@ -15,7 +15,7 @@ icon.png Apache-2.0 RSK.Audit - 2.0.0 + 3.0.0 @@ -23,16 +23,16 @@ 1591 - - + + - - + + - - - + + + diff --git a/src/Rsk.DuendeIdentityServer.AuditEventSink/Rsk.DuendeIdentityServer.AuditEventSink.csproj b/src/Rsk.DuendeIdentityServer.AuditEventSink/Rsk.DuendeIdentityServer.AuditEventSink.csproj index 38d39ad..8225ba2 100644 --- a/src/Rsk.DuendeIdentityServer.AuditEventSink/Rsk.DuendeIdentityServer.AuditEventSink.csproj +++ b/src/Rsk.DuendeIdentityServer.AuditEventSink/Rsk.DuendeIdentityServer.AuditEventSink.csproj @@ -1,22 +1,22 @@  - net6.0 + net8.0;net9.0;net10.0 Rock Solid Knowledge Ltd Duende IdentityServer event sink to add audit records into AdminUI auditing https://github.com/RockSolidKnowledge/RSK.IdentityServer4.AuditEventSink - Initial Duende IdentityServer release. + Upgrade to .NET 10 Copyright 2022 (c) Rock Solid Knowledge Ltd. All rights reserved Audit AdminUI IdentityServer Events true icon.png Apache-2.0 - 3.0.0 + 4.0.0 - + diff --git a/tests/Rsk.Audit.Tests.Common/Rsk.Audit.Tests.Common.csproj b/tests/Rsk.Audit.Tests.Common/Rsk.Audit.Tests.Common.csproj index 4b71455..7b37d1b 100644 --- a/tests/Rsk.Audit.Tests.Common/Rsk.Audit.Tests.Common.csproj +++ b/tests/Rsk.Audit.Tests.Common/Rsk.Audit.Tests.Common.csproj @@ -1,7 +1,7 @@ - netcoreapp3.1;net5.0;net6.0 + net8.0;net9.0;net10.0 diff --git a/tests/Rsk.Audit.Tests.Integration/EF/AuditDatabaseContextTests.cs b/tests/Rsk.Audit.Tests.Integration/EF/AuditDatabaseContextTests.cs new file mode 100644 index 0000000..635c2c5 --- /dev/null +++ b/tests/Rsk.Audit.Tests.Integration/EF/AuditDatabaseContextTests.cs @@ -0,0 +1,45 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using RSK.Audit.EF; +using Xunit; + +namespace Rsk.Audit.Tests.Integration.EF; + +[Collection("AuditDatabaseContextTests")] + +public class AuditDatabaseContextTests +{ + [Fact] + public void Ctor_WhenCalledWithSchema_ExpectThatSchemaSetInTheDatabase() + { + string schema = "guest"; + + var dbContextOptions = new DbContextOptionsBuilder() + .ReplaceService() + .UseInMemoryDatabase(nameof(AuditQueryIntegrationTests) + "WithSchema") + .Options; + + var databaseContext = new AuditDatabaseContext(dbContextOptions, schema); + databaseContext.Database.EnsureDeleted(); + databaseContext.Database.EnsureCreated(); + + Assert.Equal(schema, databaseContext.Schema); + Assert.Equal(schema, databaseContext.Model["Relational:DefaultSchema"]!.ToString()); + } + + [Fact] + public void Ctor_WhenCalledWithoutSchema_ExpectNoSchemaSetInTheDatabase() + { + var dbContextOptions = new DbContextOptionsBuilder() + .ReplaceService() + .UseInMemoryDatabase(nameof(AuditQueryIntegrationTests) + "WithoutSchema") + .Options; + + var databaseContext = new AuditDatabaseContext(dbContextOptions); + databaseContext.Database.EnsureDeleted(); + databaseContext.Database.EnsureCreated(); + + Assert.Null(databaseContext.Schema); + Assert.Null(databaseContext.Model["Relational:DefaultSchema"]?.ToString()); + } +} \ No newline at end of file diff --git a/tests/Rsk.Audit.Tests.Integration/EF/AuditQueryIntegrationTests.cs b/tests/Rsk.Audit.Tests.Integration/EF/AuditQueryIntegrationTests.cs index 089a228..f15a284 100644 --- a/tests/Rsk.Audit.Tests.Integration/EF/AuditQueryIntegrationTests.cs +++ b/tests/Rsk.Audit.Tests.Integration/EF/AuditQueryIntegrationTests.cs @@ -8,315 +8,314 @@ using Xunit; using AuditProviderFactory = RSK.Audit.AuditProviderFactory; -namespace Rsk.Audit.Tests.Integration.EF +namespace Rsk.Audit.Tests.Integration.EF; + +[Collection("AuditQueryIntegrationTests")] +public class AuditQueryIntegrationTests { - [Collection("AuditQueryIntegrationTests")] - public class AuditQueryIntegrationTests + private readonly AuditProviderFactory auditProviderFactory; + private readonly DbContextOptions dbContextOptions; + + public AuditQueryIntegrationTests() { - private readonly AuditProviderFactory auditProviderFactory; - private readonly DbContextOptions dbContextOptions; + dbContextOptions = new DbContextOptionsBuilder() + .UseInMemoryDatabase(nameof(AuditQueryIntegrationTests)) + .Options; - public AuditQueryIntegrationTests() - { - dbContextOptions = new DbContextOptionsBuilder() - .UseInMemoryDatabase(nameof(AuditQueryIntegrationTests)) - .Options; + var databaseContext = new AuditDatabaseContext(dbContextOptions); + databaseContext.Database.EnsureDeleted(); + databaseContext.Database.EnsureCreated(); - var databaseContext = new AuditDatabaseContext(dbContextOptions); - databaseContext.Database.EnsureDeleted(); - databaseContext.Database.EnsureCreated(); + auditProviderFactory = new RSK.Audit.EF.AuditProviderFactory(dbContextOptions); + } - auditProviderFactory = new RSK.Audit.EF.AuditProviderFactory(dbContextOptions); - } + [Fact] + public async Task GivenAQuery_ShouldReturnRecordsBetweenDatesSpecifiedAndNoMore() + { + var expectedFirstDateTime = new DateTime(2018, 3, 2, 10, 20, 10); + var auditFrequency = TimeSpan.FromSeconds(3); + var expectedNumberOfRecords = 104; + var expectedEndDateTime = expectedFirstDateTime.Add(TimeSpan.FromSeconds(auditFrequency.TotalSeconds * expectedNumberOfRecords)); - [Fact] - public async Task GivenAQuery_ShouldReturnRecordsBetweenDatesSpecifiedAndNoMore() - { - var expectedFirstDateTime = new DateTime(2018, 3, 2, 10, 20, 10); - var auditFrequency = TimeSpan.FromSeconds(3); - var expectedNumberOfRecords = 104; - var expectedEndDateTime = expectedFirstDateTime.Add(TimeSpan.FromSeconds(auditFrequency.TotalSeconds * expectedNumberOfRecords)); + WriteAuditRecords("AdminUI", "Login", expectedFirstDateTime.Subtract(TimeSpan.FromSeconds(1)), 1, auditFrequency); - WriteAuditRecords("AdminUI", "Login", expectedFirstDateTime.Subtract(TimeSpan.FromSeconds(1)), 1, auditFrequency); + var expectedRows = WriteAuditRecords("AdminUI", "Login", expectedFirstDateTime, expectedNumberOfRecords, auditFrequency); + WriteAuditRecords("AdminUI", "Login", expectedEndDateTime.AddSeconds(1), 1, auditFrequency); - var expectedRows = WriteAuditRecords("AdminUI", "Login", expectedFirstDateTime, expectedNumberOfRecords, auditFrequency); - WriteAuditRecords("AdminUI", "Login", expectedEndDateTime.AddSeconds(1), 1, auditFrequency); + var sut = CreateSut(); - var sut = CreateSut(); + var results = await sut.Between(expectedFirstDateTime, expectedEndDateTime) + .ExecuteAscending(); - var results = await sut.Between(expectedFirstDateTime, expectedEndDateTime) - .ExecuteAscending(); + Assert.Equal(expectedNumberOfRecords, results.TotalNumberOfRows); + Assert.Equal(expectedNumberOfRecords, results.Rows.Count()); + Assert.Equal(expectedRows.Select(r => new AuditEntityToEntryAdapter(r)), results.Rows); + } - Assert.Equal(expectedNumberOfRecords, results.TotalNumberOfRows); - Assert.Equal(expectedNumberOfRecords, results.Rows.Count()); - Assert.Equal(expectedRows.Select(r => new AuditEntityToEntryAdapter(r)), results.Rows); - } + [Fact] + public async Task GivenAQueryFilteringByResourceIdentifierOrName_ShouldReturnRecordsThatMatchIdentifierInGivenTimeRange() + { + var resourceIdentifierToFind = Guid.NewGuid().ToString(); + var expectedFirstDateTime = new DateTime(2018, 3, 2, 10, 20, 10); + var auditFrequency = TimeSpan.FromSeconds(3); + var expectedNumberOfRecords = 104; + var expectedEndDateTime = expectedFirstDateTime.Add(TimeSpan.FromSeconds(auditFrequency.TotalSeconds * expectedNumberOfRecords)); - [Fact] - public async Task GivenAQueryFilteringByResourceIdentifierOrName_ShouldReturnRecordsThatMatchIdentifierInGivenTimeRange() - { - var resourceIdentifierToFind = Guid.NewGuid().ToString(); - var expectedFirstDateTime = new DateTime(2018, 3, 2, 10, 20, 10); - var auditFrequency = TimeSpan.FromSeconds(3); - var expectedNumberOfRecords = 104; - var expectedEndDateTime = expectedFirstDateTime.Add(TimeSpan.FromSeconds(auditFrequency.TotalSeconds * expectedNumberOfRecords)); + WriteAuditRecords("AdminUI", "Login", expectedFirstDateTime.Subtract(TimeSpan.FromSeconds(1)), + 1, auditFrequency, resourceIdentifier: resourceIdentifierToFind); - WriteAuditRecords("AdminUI", "Login", expectedFirstDateTime.Subtract(TimeSpan.FromSeconds(1)), - 1, auditFrequency, resourceIdentifier: resourceIdentifierToFind); + WriteAuditRecords("AdminUI", "Login", expectedFirstDateTime.Subtract(TimeSpan.FromSeconds(1)), + 1, auditFrequency, resourceIdentifier: "Not required"); - WriteAuditRecords("AdminUI", "Login", expectedFirstDateTime.Subtract(TimeSpan.FromSeconds(1)), - 1, auditFrequency, resourceIdentifier: "Not required"); + WriteAuditRecords("AdminUI", "Login", expectedFirstDateTime, expectedNumberOfRecords, auditFrequency); - WriteAuditRecords("AdminUI", "Login", expectedFirstDateTime, expectedNumberOfRecords, auditFrequency); + var expectedRows = WriteAuditRecords("AdminUI", "Login", expectedFirstDateTime, expectedNumberOfRecords, auditFrequency, + resourceIdentifier: resourceIdentifierToFind); - var expectedRows = WriteAuditRecords("AdminUI", "Login", expectedFirstDateTime, expectedNumberOfRecords, auditFrequency, - resourceIdentifier: resourceIdentifierToFind); + WriteAuditRecords("AdminUI", "Login", expectedEndDateTime.AddSeconds(1), 1, auditFrequency); - WriteAuditRecords("AdminUI", "Login", expectedEndDateTime.AddSeconds(1), 1, auditFrequency); + var sut = CreateSut(); - var sut = CreateSut(); + var results = await sut.Between(expectedFirstDateTime, expectedEndDateTime) + .AndResource(Matches.Exactly, resourceIdentifierToFind) + .ExecuteAscending(); - var results = await sut.Between(expectedFirstDateTime, expectedEndDateTime) - .AndResource(Matches.Exactly, resourceIdentifierToFind) - .ExecuteAscending(); + Assert.Equal(expectedNumberOfRecords, results.TotalNumberOfRows); + Assert.Equal(expectedNumberOfRecords, results.Rows.Count()); + Assert.Equal(expectedRows.Select(r => new AuditEntityToEntryAdapter(r)), results.Rows); + } - Assert.Equal(expectedNumberOfRecords, results.TotalNumberOfRows); - Assert.Equal(expectedNumberOfRecords, results.Rows.Count()); - Assert.Equal(expectedRows.Select(r => new AuditEntityToEntryAdapter(r)), results.Rows); - } + [Fact] + public async Task GivenAQueryForPageTwo_ShouldReturnRecordsBetweenDatesSpecifiedForPageTwoAndNoMore() + { + const int page = 2; + const int pageSize = 10; + const int expectedNumberOfPages = 11; - [Fact] - public async Task GivenAQueryForPageTwo_ShouldReturnRecordsBetweenDatesSpecifiedForPageTwoAndNoMore() - { - const int page = 2; - const int pageSize = 10; - const int expectedNumberOfPages = 11; + var expectedFirstDateTime = new DateTime(2018, 3, 2, 10, 20, 10); + var auditFrequency = TimeSpan.FromSeconds(3); + var expectedNumberOfRecords = 104; + var expectedEndDateTime = expectedFirstDateTime.Add(TimeSpan.FromSeconds(auditFrequency.TotalSeconds * expectedNumberOfRecords)); - var expectedFirstDateTime = new DateTime(2018, 3, 2, 10, 20, 10); - var auditFrequency = TimeSpan.FromSeconds(3); - var expectedNumberOfRecords = 104; - var expectedEndDateTime = expectedFirstDateTime.Add(TimeSpan.FromSeconds(auditFrequency.TotalSeconds * expectedNumberOfRecords)); + WriteAuditRecords("AdminUI", "Login", expectedFirstDateTime.Subtract(TimeSpan.FromSeconds(1)), + 1, auditFrequency); - WriteAuditRecords("AdminUI", "Login", expectedFirstDateTime.Subtract(TimeSpan.FromSeconds(1)), - 1, auditFrequency); + var expectedRows = + WriteAuditRecords("AdminUI", "Login", expectedFirstDateTime, expectedNumberOfRecords, auditFrequency) + .OrderByDescending(ae => ae.When); + var expectedPage = expectedRows.Skip((page - 1) * pageSize).Take(pageSize); - var expectedRows = - WriteAuditRecords("AdminUI", "Login", expectedFirstDateTime, expectedNumberOfRecords, auditFrequency) - .OrderByDescending(ae => ae.When); - var expectedPage = expectedRows.Skip((page - 1) * pageSize).Take(pageSize); + WriteAuditRecords("AdminUI", "Login", expectedEndDateTime.AddSeconds(1), 1, auditFrequency); - WriteAuditRecords("AdminUI", "Login", expectedEndDateTime.AddSeconds(1), 1, auditFrequency); + var sut = CreateSut(); - var sut = CreateSut(); + var results = await sut.Between(expectedFirstDateTime, expectedEndDateTime, page, pageSize) + .ExecuteDescending(); - var results = await sut.Between(expectedFirstDateTime, expectedEndDateTime, page, pageSize) - .ExecuteDescending(); + Assert.Equal(expectedNumberOfRecords, results.TotalNumberOfRows); + Assert.Equal(page, results.Page); + Assert.Equal(expectedNumberOfPages, results.TotalNumberOfPages); + Assert.Equal(expectedPage.ToAuditEntries(), results.Rows); + } - Assert.Equal(expectedNumberOfRecords, results.TotalNumberOfRows); - Assert.Equal(page, results.Page); - Assert.Equal(expectedNumberOfPages, results.TotalNumberOfPages); - Assert.Equal(expectedPage.ToAuditEntries(), results.Rows); - } + [Fact] + public async Task GivenAQuerySortedBySubject_ShouldReturnRecordsSortedBySubject() + { + var auditFrequency = TimeSpan.FromSeconds(3); + var startTime = DateTime.Now; - [Fact] - public async Task GivenAQuerySortedBySubject_ShouldReturnRecordsSortedBySubject() - { - var auditFrequency = TimeSpan.FromSeconds(3); - var startTime = DateTime.Now; + var fredRows = WriteAuditRecords("AdminUI", "Login", + startTime, + 5, auditFrequency, subject: "fred"); - var fredRows = WriteAuditRecords("AdminUI", "Login", - startTime, - 5, auditFrequency, subject: "fred"); + var andyRows = WriteAuditRecords("AdminUI", "Login", + DateTime.Now, + 5, auditFrequency, subject: "andy"); - var andyRows = WriteAuditRecords("AdminUI", "Login", - DateTime.Now, - 5, auditFrequency, subject: "andy"); + var expectedRows = fredRows.Concat(andyRows).OrderBy(ae => ae.SubjectIdentifier).ToList(); - var expectedRows = fredRows.Concat(andyRows).OrderBy(ae => ae.SubjectIdentifier).ToList(); + var sut = CreateSut(); - var sut = CreateSut(); + var results = await sut.Between(startTime, expectedRows.Last().When.ToLocalTime().AddSeconds(3)) + .ExecuteAscending(AuditQuerySort.Subject); - var results = await sut.Between(startTime, expectedRows.Last().When.ToLocalTime().AddSeconds(3)) - .ExecuteAscending(AuditQuerySort.Subject); + var expectedNumberOfRecords = expectedRows.Count(); - var expectedNumberOfRecords = expectedRows.Count(); + Assert.Equal(expectedNumberOfRecords, results.TotalNumberOfRows); + Assert.Equal(expectedNumberOfRecords, results.Rows.Count()); + Assert.Equal(expectedRows.ToAuditEntries(), results.Rows); + } - Assert.Equal(expectedNumberOfRecords, results.TotalNumberOfRows); - Assert.Equal(expectedNumberOfRecords, results.Rows.Count()); - Assert.Equal(expectedRows.ToAuditEntries(), results.Rows); - } + [Fact] + public async Task GivenAQueryFilteredByAction_ShouldReturnRecordsMatchingAction() + { + var auditFrequency = TimeSpan.FromSeconds(3); + var startTime = DateTime.Now; - [Fact] - public async Task GivenAQueryFilteredByAction_ShouldReturnRecordsMatchingAction() - { - var auditFrequency = TimeSpan.FromSeconds(3); - var startTime = DateTime.Now; + var loginRows = WriteAuditRecords("AdminUI", "Login", + startTime, + 5, auditFrequency, subject: "fred"); - var loginRows = WriteAuditRecords("AdminUI", "Login", - startTime, - 5, auditFrequency, subject: "fred"); + var logoutRows = WriteAuditRecords("AdminUI", "Logout", + DateTime.Now, + 5, auditFrequency, subject: "andy"); - var logoutRows = WriteAuditRecords("AdminUI", "Logout", - DateTime.Now, - 5, auditFrequency, subject: "andy"); + var sut = CreateSut(); - var sut = CreateSut(); + var results = await sut.Between(startTime, loginRows.Last().When.ToLocalTime().AddSeconds(3)) + .AndAction(Matches.SomethingLike, "Logi") + .ExecuteAscending(AuditQuerySort.Subject); - var results = await sut.Between(startTime, loginRows.Last().When.ToLocalTime().AddSeconds(3)) - .AndAction(Matches.SomethingLike, "Logi") - .ExecuteAscending(AuditQuerySort.Subject); + var expectedNumberOfRecords = loginRows.Count(); - var expectedNumberOfRecords = loginRows.Count(); + Assert.Equal(expectedNumberOfRecords, results.TotalNumberOfRows); + Assert.Equal(expectedNumberOfRecords, results.Rows.Count()); + Assert.Equal(loginRows.ToAuditEntries(), results.Rows); + } - Assert.Equal(expectedNumberOfRecords, results.TotalNumberOfRows); - Assert.Equal(expectedNumberOfRecords, results.Rows.Count()); - Assert.Equal(loginRows.ToAuditEntries(), results.Rows); - } + [Fact] + public async Task GivenAQueryFilteredByAction_ShouldReturnRecordsMatchingActionsIgnoringCase() + { + var auditFrequency = TimeSpan.FromSeconds(3); + var startTime = DateTime.Now; - [Fact] - public async Task GivenAQueryFilteredByAction_ShouldReturnRecordsMatchingActionsIgnoringCase() - { - var auditFrequency = TimeSpan.FromSeconds(3); - var startTime = DateTime.Now; + var loginRows = WriteAuditRecords("AdminUI", "Login", + startTime, + 5, auditFrequency, subject: "fred"); - var loginRows = WriteAuditRecords("AdminUI", "Login", - startTime, - 5, auditFrequency, subject: "fred"); + var logoutRows = WriteAuditRecords("AdminUI", "Logout", + DateTime.Now, + 5, auditFrequency, subject: "andy"); - var logoutRows = WriteAuditRecords("AdminUI", "Logout", - DateTime.Now, - 5, auditFrequency, subject: "andy"); + var sut = CreateSut(); - var sut = CreateSut(); + var results = await sut.Between(startTime, loginRows.Last().When.ToLocalTime().AddSeconds(3)) + .AndAction(Matches.StartsWith, "logi") + .ExecuteAscending(AuditQuerySort.Subject); - var results = await sut.Between(startTime, loginRows.Last().When.ToLocalTime().AddSeconds(3)) - .AndAction(Matches.StartsWith, "logi") - .ExecuteAscending(AuditQuerySort.Subject); + var expectedNumberOfRecords = loginRows.Count(); - var expectedNumberOfRecords = loginRows.Count(); + Assert.Equal(expectedNumberOfRecords, results.TotalNumberOfRows); + Assert.Equal(expectedNumberOfRecords, results.Rows.Count()); + Assert.Equal(loginRows.ToAuditEntries(), results.Rows); + } - Assert.Equal(expectedNumberOfRecords, results.TotalNumberOfRows); - Assert.Equal(expectedNumberOfRecords, results.Rows.Count()); - Assert.Equal(loginRows.ToAuditEntries(), results.Rows); - } + [Fact] + public async Task GivenAQueryFilteredBySubject_ShouldReturnRecordsMatchingSubjectIgnoringCase() + { + var auditFrequency = TimeSpan.FromSeconds(3); + var startTime = DateTime.Now; - [Fact] - public async Task GivenAQueryFilteredBySubject_ShouldReturnRecordsMatchingSubjectIgnoringCase() - { - var auditFrequency = TimeSpan.FromSeconds(3); - var startTime = DateTime.Now; + var loginRows = WriteAuditRecords("AdminUI", "Login", + startTime, + 5, auditFrequency, subject: "fred"); - var loginRows = WriteAuditRecords("AdminUI", "Login", - startTime, - 5, auditFrequency, subject: "fred"); + var logoutRows = WriteAuditRecords("AdminUI", "Logout", + DateTime.Now, + 5, auditFrequency, subject: "andy"); - var logoutRows = WriteAuditRecords("AdminUI", "Logout", - DateTime.Now, - 5, auditFrequency, subject: "andy"); + var sut = CreateSut(); - var sut = CreateSut(); + var results = await sut.Between(startTime, loginRows.Last().When.ToLocalTime().AddSeconds(3)) + .AndSubject(Matches.StartsWith, "fred") + .ExecuteAscending(AuditQuerySort.Subject); - var results = await sut.Between(startTime, loginRows.Last().When.ToLocalTime().AddSeconds(3)) - .AndSubject(Matches.StartsWith, "fred") - .ExecuteAscending(AuditQuerySort.Subject); + var expectedNumberOfRecords = loginRows.Count(); - var expectedNumberOfRecords = loginRows.Count(); + Assert.Equal(expectedNumberOfRecords, results.TotalNumberOfRows); + Assert.Equal(expectedNumberOfRecords, results.Rows.Count()); + Assert.Equal(loginRows.ToAuditEntries(), results.Rows); + } - Assert.Equal(expectedNumberOfRecords, results.TotalNumberOfRows); - Assert.Equal(expectedNumberOfRecords, results.Rows.Count()); - Assert.Equal(loginRows.ToAuditEntries(), results.Rows); - } + [Fact] + public async Task GivenAQueryFilteredBySource_ShouldReturnRecordsMatchingSourceIgnoringCase() + { + var auditFrequency = TimeSpan.FromSeconds(3); + var startTime = DateTime.Now; - [Fact] - public async Task GivenAQueryFilteredBySource_ShouldReturnRecordsMatchingSourceIgnoringCase() - { - var auditFrequency = TimeSpan.FromSeconds(3); - var startTime = DateTime.Now; + var loginRows = WriteAuditRecords("AdminUI", "Login", + startTime, + 5, auditFrequency, subject: "fred"); - var loginRows = WriteAuditRecords("AdminUI", "Login", - startTime, - 5, auditFrequency, subject: "fred"); + var sut = CreateSut(); - var sut = CreateSut(); + var results = await sut.Between(startTime, loginRows.Last().When.ToLocalTime().AddSeconds(3)) + .AndSource(Matches.StartsWith, "adminui") + .ExecuteAscending(AuditQuerySort.Subject); - var results = await sut.Between(startTime, loginRows.Last().When.ToLocalTime().AddSeconds(3)) - .AndSource(Matches.StartsWith, "adminui") - .ExecuteAscending(AuditQuerySort.Subject); + var expectedNumberOfRecords = loginRows.Count(); - var expectedNumberOfRecords = loginRows.Count(); + Assert.Equal(expectedNumberOfRecords, results.TotalNumberOfRows); + Assert.Equal(expectedNumberOfRecords, results.Rows.Count()); + Assert.Equal(loginRows.ToAuditEntries(), results.Rows); + } - Assert.Equal(expectedNumberOfRecords, results.TotalNumberOfRows); - Assert.Equal(expectedNumberOfRecords, results.Rows.Count()); - Assert.Equal(loginRows.ToAuditEntries(), results.Rows); - } + [Fact] + public async Task GivenAQueryFilteredByResource_ShouldReturnRecordsMatchingResourceIgnoringCase() + { + var auditFrequency = TimeSpan.FromSeconds(3); + var startTime = DateTime.Now; - [Fact] - public async Task GivenAQueryFilteredByResource_ShouldReturnRecordsMatchingResourceIgnoringCase() - { - var auditFrequency = TimeSpan.FromSeconds(3); - var startTime = DateTime.Now; + var loginRows = WriteAuditRecords("AdminUI", "Login", + startTime, + 5, auditFrequency, subject: "fred", resource: "admin_ui"); - var loginRows = WriteAuditRecords("AdminUI", "Login", - startTime, - 5, auditFrequency, subject: "fred", resource: "admin_ui"); + var sut = CreateSut(); - var sut = CreateSut(); + var results = await sut.Between(startTime, loginRows.Last().When.ToLocalTime().AddSeconds(3)) + .AndResource(Matches.StartsWith, "Admin_UI") + .ExecuteAscending(AuditQuerySort.Subject); - var results = await sut.Between(startTime, loginRows.Last().When.ToLocalTime().AddSeconds(3)) - .AndResource(Matches.StartsWith, "Admin_UI") - .ExecuteAscending(AuditQuerySort.Subject); + var expectedNumberOfRecords = loginRows.Count(); - var expectedNumberOfRecords = loginRows.Count(); + Assert.Equal(expectedNumberOfRecords, results.TotalNumberOfRows); + Assert.Equal(expectedNumberOfRecords, results.Rows.Count()); + Assert.Equal(loginRows.ToAuditEntries(), results.Rows); + } - Assert.Equal(expectedNumberOfRecords, results.TotalNumberOfRows); - Assert.Equal(expectedNumberOfRecords, results.Rows.Count()); - Assert.Equal(loginRows.ToAuditEntries(), results.Rows); - } + private IQueryableAuditableActions CreateSut() + { + return auditProviderFactory.CreateAuditQuery(); + } - private IQueryableAuditableActions CreateSut() - { - return auditProviderFactory.CreateAuditQuery(); - } + private List WriteAuditRecords(string source, string action, DateTime from, int number, TimeSpan frequency, + bool success = true, string subject = "andy", string subjectIdentifier = "3232-232198", string description = "", + string resource = "Some Resource", string resourceType = "client", string resourceIdentifier = "342432-3256-4624") + { + var auditEntries = new List(); + DateTime when = from; - private List WriteAuditRecords(string source, string action, DateTime from, int number, TimeSpan frequency, - bool success = true, string subject = "andy", string subjectIdentifier = "3232-232198", string description = "", - string resource = "Some Resource", string resourceType = "client", string resourceIdentifier = "342432-3256-4624") + using (var context = new AuditDatabaseContext(dbContextOptions)) { - var auditEntries = new List(); - DateTime when = from; - - using (var context = new AuditDatabaseContext(dbContextOptions)) + for (int nAuditRecordIndex = 0; nAuditRecordIndex < number; nAuditRecordIndex++) { - for (int nAuditRecordIndex = 0; nAuditRecordIndex < number; nAuditRecordIndex++) + var nextAuditEntry = new AuditEntity() { - var nextAuditEntry = new AuditEntity() - { - When = when.ToUniversalTime(), - Succeeded = success, - Subject = subject, - SubjectIdentifier = subjectIdentifier, - SubjectType = "User", - Source = source, - Description = description, - Action = action, - Resource = resource, - ResourceType = resourceType, - ResourceIdentifier = resourceIdentifier - }; - - auditEntries.Add(nextAuditEntry); - - when = when.Add(frequency); - } - - context.AuditEntries.AddRange(auditEntries); - - context.SaveChanges(); + When = when.ToUniversalTime(), + Succeeded = success, + Subject = subject, + SubjectIdentifier = subjectIdentifier, + SubjectType = "User", + Source = source, + Description = description, + Action = action, + Resource = resource, + ResourceType = resourceType, + ResourceIdentifier = resourceIdentifier + }; + + auditEntries.Add(nextAuditEntry); + + when = when.Add(frequency); } - return auditEntries; + context.AuditEntries.AddRange(auditEntries); + + context.SaveChanges(); } + + return auditEntries; } } \ No newline at end of file diff --git a/tests/Rsk.Audit.Tests.Integration/EF/TestModelCacheKeyFactory.cs b/tests/Rsk.Audit.Tests.Integration/EF/TestModelCacheKeyFactory.cs new file mode 100644 index 0000000..8580c9b --- /dev/null +++ b/tests/Rsk.Audit.Tests.Integration/EF/TestModelCacheKeyFactory.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.InMemory.Infrastructure.Internal; + +namespace Rsk.Audit.Tests.Integration.EF; + +public class TestModelCacheKeyFactory : IModelCacheKeyFactory +{ + public object Create(DbContext context, bool designTime) + { + // Extract connection string from options without accessing the database + var connectionString = string.Empty; + var extension = context.GetService() + ?.FindExtension(); + if (extension != null) + { + connectionString = extension.StoreName ?? string.Empty; + } + + return (context.GetType(), connectionString, designTime); + } +} \ No newline at end of file diff --git a/tests/Rsk.Audit.Tests.Integration/Rsk.Audit.Tests.Integration.csproj b/tests/Rsk.Audit.Tests.Integration/Rsk.Audit.Tests.Integration.csproj index a30b8b7..6ea67ea 100644 --- a/tests/Rsk.Audit.Tests.Integration/Rsk.Audit.Tests.Integration.csproj +++ b/tests/Rsk.Audit.Tests.Integration/Rsk.Audit.Tests.Integration.csproj @@ -1,7 +1,7 @@ - netcoreapp3.1;net5.0;net6.0 + net8.0;net9.0;net10.0 false @@ -20,20 +20,16 @@ - - + + - - + + - - - - - - + + diff --git a/tests/Rsk.Audit.Tests/Rsk.Audit.Tests.csproj b/tests/Rsk.Audit.Tests/Rsk.Audit.Tests.csproj index 74f9739..1d34b74 100644 --- a/tests/Rsk.Audit.Tests/Rsk.Audit.Tests.csproj +++ b/tests/Rsk.Audit.Tests/Rsk.Audit.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1;net5.0;net6.0 + net8.0;net9.0;net10.0 false diff --git a/tests/Rsk.DuendeIdentityServer.AuditEventSink.Tests/Rsk.DuendeIdentityServer.AuditEventSink.Tests.csproj b/tests/Rsk.DuendeIdentityServer.AuditEventSink.Tests/Rsk.DuendeIdentityServer.AuditEventSink.Tests.csproj index bff0e47..52125b9 100644 --- a/tests/Rsk.DuendeIdentityServer.AuditEventSink.Tests/Rsk.DuendeIdentityServer.AuditEventSink.Tests.csproj +++ b/tests/Rsk.DuendeIdentityServer.AuditEventSink.Tests/Rsk.DuendeIdentityServer.AuditEventSink.Tests.csproj @@ -1,7 +1,7 @@ - net6.0 + net8.0;net9.0;net10.0 false diff --git a/tests/Rsk.IdentityServer4.AuditEventSink.Tests/RSK.IdentityServer4.AuditEventSink.Tests.csproj b/tests/Rsk.IdentityServer4.AuditEventSink.Tests/RSK.IdentityServer4.AuditEventSink.Tests.csproj index 1207153..8f504d1 100644 --- a/tests/Rsk.IdentityServer4.AuditEventSink.Tests/RSK.IdentityServer4.AuditEventSink.Tests.csproj +++ b/tests/Rsk.IdentityServer4.AuditEventSink.Tests/RSK.IdentityServer4.AuditEventSink.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net8.0;net9.0;net10.0 false