Skip to content

Commit

Permalink
Fixup
Browse files Browse the repository at this point in the history
  • Loading branch information
w1am committed Jan 8, 2025
1 parent b278f7d commit eb68a7f
Show file tree
Hide file tree
Showing 31 changed files with 616 additions and 53 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
fail-fast: false
matrix:
docker-tag: [ ci, lts, previous-lts ]
test: [ Streams, PersistentSubscriptions, Operations, Projections, Security, Misc ]
test: [ Streams, PersistentSubscriptions, Operations, ProjectionManagement, Security, Misc ]
name: Test CE (${{ matrix.docker-tag }})
with:
docker-tag: ${{ matrix.docker-tag }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/dispatch-ce.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
strategy:
fail-fast: false
matrix:
test: [ Streams, PersistentSubscriptions, Operations, Projections, Security, Misc ]
test: [ Streams, PersistentSubscriptions, Operations, ProjectionManagement, Security, Misc ]
name: Test CE (${{ inputs.docker-tag }})
with:
docker-tag: ${{ inputs.docker-tag }}
Expand Down
28 changes: 7 additions & 21 deletions src/Kurrent.Client/Core/Certificates/X509Certificates.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
#pragma warning disable SYSLIB0057

#if NET48
using Org.BouncyCastle.Crypto;
Expand All @@ -13,35 +14,20 @@
namespace EventStore.Client;

static class X509Certificates {
// TODO SS: Use .NET 8 X509Certificate2.CreateFromPemFile(certPemFilePath, keyPemFilePath) once the Windows32Exception issue is resolved
public static X509Certificate2 CreateFromPemFile(string certPemFilePath, string keyPemFilePath) {
try {
#if NET9_0_OR_GREATER
using var publicCert = X509CertificateLoader.LoadCertificateFromFile(certPemFilePath);
#if NET8_0_OR_GREATER
using var certificate = X509Certificate2.CreateFromPemFile(certPemFilePath, keyPemFilePath);
#else
using var publicCert = new X509Certificate2(certPemFilePath);
#endif
using var privateKey = RSA.Create().ImportPrivateKeyFromFile(keyPemFilePath);
using var privateKey = RSA.Create().ImportPrivateKeyFromFile(keyPemFilePath);
using var certificate = publicCert.CopyWithPrivateKey(privateKey);

#if NET48
return new(certificate.Export(X509ContentType.Pfx));
#else
return X509Certificate2.CreateFromPemFile(certPemFilePath, keyPemFilePath);
#endif

return new X509Certificate2(certificate.Export(X509ContentType.Pfx));
} catch (Exception ex) {
throw new CryptographicException($"Failed to load private key: {ex.Message}");
}

// Notes:
// using X509Certificate2.CreateFromPemFile(certPemFilePath, keyPemFilePath) would be the ideal choice here,
// but it's currently causing a Win32Exception specifically on Windows. Alternative implementation is used until the issue is resolved.
//
// Error: The SSL connection could not be established, see inner exception. AuthenticationException: Authentication failed because the platform
// does not support ephemeral keys. Win32Exception: No credentials are available in the security package
//
// public static X509Certificate2 CreateFromPemFile(string certPemFilePath, string keyPemFilePath) =>
// X509Certificate2.CreateFromPemFile(certPemFilePath, keyPemFilePath);
}
}

Expand All @@ -66,7 +52,7 @@ public static RSA ImportPrivateKeyFromFile(this RSA rsa, string privateKeyPath)
public static RSA ImportPrivateKeyFromFile(this RSA rsa, string privateKeyPath) {
var (content, label) = LoadPemKeyFile(privateKeyPath);

var privateKey = string.Join(string.Empty, content[1..^1]);
var privateKey = string.Join(string.Empty, content[1..^1]);
var privateKeyBytes = Convert.FromBase64String(privateKey);

if (label == RsaPemLabels.Pkcs8PrivateKey)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,8 +317,7 @@ static void ConfigureClientCertificate(KurrentClientSettings settings, IReadOnly
);

try {
settings.ConnectivitySettings.ClientCertificate =
X509Certificates.CreateFromPemFile(certPemFilePath, keyPemFilePath);
settings.ConnectivitySettings.ClientCertificate = X509Certificates.CreateFromPemFile(certPemFilePath, keyPemFilePath);
} catch (Exception ex) {
throw new InvalidClientCertificateException("Failed to create client certificate.", ex);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ public static KurrentFixtureOptions DefaultOptions() {
["EVENTSTORE_ADVERTISE_HTTP_PORT_TO_CLIENT_AS"] = $"{NetworkPortProvider.DefaultEsdbPort}"
};

if (GlobalEnvironment.DockerImage.Contains("commercial")) {
defaultEnvironment["EVENTSTORE_TRUSTED_ROOT_CERTIFICATES_PATH"] = "/etc/eventstore/certs/ca";
defaultEnvironment["EventStore__Plugins__UserCertificates__Enabled"] = "true";
}

if (port != NetworkPortProvider.DefaultEsdbPort) {
if (GlobalEnvironment.Variables.TryGetValue("ES_DOCKER_TAG", out var tag) && tag == "ci")
defaultEnvironment["EVENTSTORE_ADVERTISE_NODE_PORT_TO_CLIENT_AS"] = $"{port}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ public static KurrentFixtureOptions DefaultOptions() {
["EVENTSTORE_ADVERTISE_HTTP_PORT_TO_CLIENT_AS"] = $"{NetworkPortProvider.DefaultEsdbPort}"
};

if (GlobalEnvironment.DockerImage.Contains("commercial")) {
defaultEnvironment["EVENTSTORE_TRUSTED_ROOT_CERTIFICATES_PATH"] = "/etc/eventstore/certs/ca";
defaultEnvironment["EventStore__Plugins__UserCertificates__Enabled"] = "true";
}

if (port != NetworkPortProvider.DefaultEsdbPort) {
if (GlobalEnvironment.Variables.TryGetValue("ES_DOCKER_TAG", out var tag) && tag == "ci")
defaultEnvironment["EVENTSTORE_ADVERTISE_NODE_PORT_TO_CLIENT_AS"] = $"{port}";
Expand Down Expand Up @@ -181,7 +186,7 @@ async Task<int> GetNextAvailablePort(TimeSpan delay = default) {
#if NET
if (socket.Connected) await socket.DisconnectAsync(true);
#else
if (socket.Connected) socket.Disconnect(true);
if (socket.Connected) socket.Disconnect(true);
#endif
}
}
Expand Down
29 changes: 18 additions & 11 deletions test/Kurrent.Client.Tests/ClientCertificatesTests.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
using EventStore.Client;
using Humanizer;
using Kurrent.Client.Tests.TestNode;

namespace Kurrent.Client.Tests;

[Trait("Category", "Target:Misc")]
[Trait("Category", "Target:Plugins")]
[Trait("Category", "Type:UserCertificate")]
public class ClientCertificateTests(ITestOutputHelper output, KurrentPermanentFixture fixture)
: KurrentPermanentTests<KurrentPermanentFixture>(output, fixture) {
public class ClientCertificateTests(ITestOutputHelper output, KurrentTemporaryFixture fixture)
: KurrentTemporaryTests<KurrentTemporaryFixture>(output, fixture) {
[SupportsPlugins.Theory(EventStoreRepository.Commercial, "This server version does not support plugins"), BadClientCertificatesTestCases]
async Task bad_certificates_combinations_should_return_authentication_error(string userCertFile, string userKeyFile, string tlsCaFile) {
var stream = Fixture.GetStreamName();
var seedEvents = Fixture.CreateTestEvents();
var connectionString = $"esdb://localhost:2113/?tls=true&userCertFile={userCertFile}&userKeyFile={userKeyFile}&tlsCaFile={tlsCaFile}";
var stream = Fixture.GetStreamName();
var seedEvents = Fixture.CreateTestEvents();
var port = Fixture.Options.ClientSettings.ConnectivitySettings.ResolvedAddressOrDefault.Port;

var connectionString = $"esdb://localhost:{port}/?tls=true&userCertFile={userCertFile}&userKeyFile={userKeyFile}&tlsCaFile={tlsCaFile}";

var settings = KurrentClientSettings.Create(connectionString);
settings.ConnectivitySettings.TlsVerifyCert.ShouldBeTrue();
Expand All @@ -24,9 +27,11 @@ async Task bad_certificates_combinations_should_return_authentication_error(stri

[SupportsPlugins.Theory(EventStoreRepository.Commercial, "This server version does not support plugins"), ValidClientCertificatesTestCases]
async Task valid_certificates_combinations_should_write_to_stream(string userCertFile, string userKeyFile, string tlsCaFile) {
var stream = Fixture.GetStreamName();
var seedEvents = Fixture.CreateTestEvents();
var connectionString = $"esdb://localhost:2113/?userCertFile={userCertFile}&userKeyFile={userKeyFile}&tlsCaFile={tlsCaFile}";
var stream = Fixture.GetStreamName();
var seedEvents = Fixture.CreateTestEvents();
var port = Fixture.Options.ClientSettings.ConnectivitySettings.ResolvedAddressOrDefault.Port;

var connectionString = $"esdb://localhost:{port}/?userCertFile={userCertFile}&userKeyFile={userKeyFile}&tlsCaFile={tlsCaFile}";

var settings = KurrentClientSettings.Create(connectionString);
settings.ConnectivitySettings.TlsVerifyCert.ShouldBeTrue();
Expand All @@ -39,9 +44,11 @@ async Task valid_certificates_combinations_should_write_to_stream(string userCer

[SupportsPlugins.Theory(EventStoreRepository.Commercial, "This server version does not support plugins"), BadClientCertificatesTestCases]
async Task basic_authentication_should_take_precedence(string userCertFile, string userKeyFile, string tlsCaFile) {
var stream = Fixture.GetStreamName();
var seedEvents = Fixture.CreateTestEvents();
var connectionString = $"esdb://admin:changeit@localhost:2113/?userCertFile={userCertFile}&userKeyFile={userKeyFile}&tlsCaFile={tlsCaFile}";
var stream = Fixture.GetStreamName();
var seedEvents = Fixture.CreateTestEvents();
var port = Fixture.Options.ClientSettings.ConnectivitySettings.ResolvedAddressOrDefault.Port;

var connectionString = $"esdb://admin:changeit@localhost:{port}/?userCertFile={userCertFile}&userKeyFile={userKeyFile}&tlsCaFile={tlsCaFile}";

var settings = KurrentClientSettings.Create(connectionString);
settings.ConnectivitySettings.TlsVerifyCert.ShouldBeTrue();
Expand Down
28 changes: 28 additions & 0 deletions test/Kurrent.Client.Tests/InvalidCredentialsTestCases.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System.Collections;
using EventStore.Client;

namespace Kurrent.Client.Tests;

public abstract record InvalidCredentialsTestCase(TestUser User, Type ExpectedException);

public class InvalidCredentialsTestCases : IEnumerable<object?[]> {
public IEnumerator<object?[]> GetEnumerator() {
yield return new object?[] { new MissingCredentials() };
yield return new object?[] { new WrongUsername() };
yield return new object?[] { new WrongPassword() };
}

IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

public record MissingCredentials() : InvalidCredentialsTestCase(Fakers.Users.WithNoCredentials(), typeof(AccessDeniedException)) {
public override string ToString() => nameof(MissingCredentials);
}

public record WrongUsername() : InvalidCredentialsTestCase(Fakers.Users.WithInvalidCredentials(false), typeof(NotAuthenticatedException)) {
public override string ToString() => nameof(WrongUsername);
}

public record WrongPassword() : InvalidCredentialsTestCase(Fakers.Users.WithInvalidCredentials(wrongPassword: false), typeof(NotAuthenticatedException)) {
public override string ToString() => nameof(WrongPassword);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ await Assert.ThrowsAsync<TimeoutException>(
.Where(e => !SystemStreams.IsSystemStream(e.ResolvedEvent.OriginalStreamId))
.AnyAsync()
.AsTask()
.WithTimeout(TimeSpan.FromMilliseconds(250))
.WithTimeout(TimeSpan.FromSeconds(30))
);
}

Expand Down Expand Up @@ -164,7 +164,7 @@ await Assert.ThrowsAsync<TimeoutException>(
.Where(e => !SystemStreams.IsSystemStream(e.ResolvedEvent.OriginalStreamId))
.AnyAsync()
.AsTask()
.WithTimeout(TimeSpan.FromMilliseconds(250))
.WithTimeout(TimeSpan.FromSeconds(30))
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ await Fixture.Subscriptions.CreateToStreamAsync(
);

await Assert.ThrowsAsync<TimeoutException>(

Check failure on line 135 in test/Kurrent.Client.Tests/PersistentSubscriptions/SubscribeToStreamTests.cs

View workflow job for this annotation

GitHub Actions / Test CE (ci) / PersistentSubscriptions (ubuntu-latest, net9.0)

Kurrent.Client.Tests.PersistentSubscriptions.SubscribeToStreamTests.connect_to_existing_with_start_from_beginning_and_no_streamconnect_to_existing_with_start_from_not_set_and_events_in_it

Assert.Throws() Failure: Exception type was not an exact match Expected: typeof(System.TimeoutException) Actual: typeof(EventStore.Client.PersistentSubscriptionDroppedByServerException) ---- EventStore.Client.PersistentSubscriptionDroppedByServerException : Subscription group 'group-connect_to_existing_with_start_from_beginning_and_no_streamconnect_to_existing_with_start_from_not_set_and_events_in_it-ebe43b3370494324a16eb6b0b2dc3e4a' on stream 'stream-connect_to_existing_with_start_from_beginning_and_no_streamconnect_to_existing_with_start_from_not_set_and_events_in_it-9b4f26dc0b7b4d4b9a0c24e1817bf015' was dropped. -------- Grpc.Core.RpcException : Status(StatusCode="Cancelled", Detail="Subscription group group-connect_to_existing_with_start_from_beginning_and_no_streamconnect_to_existing_with_start_from_not_set_and_events_in_it-ebe43b3370494324a16eb6b0b2dc3e4a on stream stream-connect_to_existing_with_start_from_beginning_and_no_streamconnect_to_existing_with_start_from_not_set_and_events_in_it-9b4f26dc0b7b4d4b9a0c24e1817bf015 was dropped.")
() => subscription.Messages.AnyAsync(message => message is PersistentSubscriptionMessage.Event).AsTask().WithTimeout(TimeSpan.FromMilliseconds(250))
() => subscription.Messages.AnyAsync(message => message is PersistentSubscriptionMessage.Event).AsTask().WithTimeout(TimeSpan.FromSeconds(30))
);
}

Expand Down Expand Up @@ -188,7 +188,7 @@ await Fixture.Subscriptions.CreateToStreamAsync(
);

await Assert.ThrowsAsync<TimeoutException>(
() => subscription.Messages.AnyAsync(message => message is PersistentSubscriptionMessage.Event).AsTask().WithTimeout(TimeSpan.FromMilliseconds(250))
() => subscription.Messages.AnyAsync(message => message is PersistentSubscriptionMessage.Event).AsTask().WithTimeout(TimeSpan.FromSeconds(30))
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Kurrent.Client.Tests.Projections;

[Trait("Category", "Target:Projections")]
[Trait("Category", "Target:ProjectionManagement")]
public class DisableProjectionTests(ITestOutputHelper output, DisableProjectionTests.CustomFixture fixture)
: KurrentTemporaryTests<DisableProjectionTests.CustomFixture>(output, fixture) {
[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Kurrent.Client.Tests.Projections;

[Trait("Category", "Target:Projections")]
[Trait("Category", "Target:ProjectionManagement")]
public class EnableProjectionTests(ITestOutputHelper output, EnableProjectionTests.CustomFixture fixture)
: KurrentTemporaryTests<EnableProjectionTests.CustomFixture>(output, fixture) {
[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace Kurrent.Client.Tests.Projections;

[Trait("Category", "Target:Projections")]
[Trait("Category", "Target:ProjectionManagement")]
public class GetProjectionResultTests(ITestOutputHelper output, GetProjectionResultTests.CustomFixture fixture)
: KurrentTemporaryTests<GetProjectionResultTests.CustomFixture>(output, fixture) {
[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace Kurrent.Client.Tests.Projections;

[Trait("Category", "Target:Projections")]
[Trait("Category", "Target:ProjectionManagement")]
public class GetProjectionStateTests(ITestOutputHelper output, GetProjectionStateTests.CustomFixture fixture)
: KurrentTemporaryTests<GetProjectionStateTests.CustomFixture>(output, fixture) {
[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Kurrent.Client.Tests.Projections;

[Trait("Category", "Target:Projections")]
[Trait("Category", "Target:ProjectionManagement")]
public class GetProjectionStatusTests(ITestOutputHelper output, GetProjectionStatusTests.CustomFixture fixture)
: KurrentTemporaryTests<GetProjectionStatusTests.CustomFixture>(output, fixture) {
[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Kurrent.Client.Tests;

[Trait("Category", "Target:Projections")]
[Trait("Category", "Target:ProjectionManagement")]
public class ListAllProjectionsTests(ITestOutputHelper output, ListAllProjectionsTests.CustomFixture fixture)
: KurrentTemporaryTests<ListAllProjectionsTests.CustomFixture>(output, fixture) {
[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Kurrent.Client.Tests;

[Trait("Category", "Target:Projections")]
[Trait("Category", "Target:ProjectionManagement")]
public class ListContinuousProjectionsTests(ITestOutputHelper output, ListContinuousProjectionsTests.CustomFixture fixture)
: KurrentTemporaryTests<ListContinuousProjectionsTests.CustomFixture>(output, fixture) {
[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Kurrent.Client.Tests.Projections;

[Trait("Category", "Target:Projections")]
[Trait("Category", "Target:ProjectionManagement")]
public class ListOneTimeProjectionsTests(ITestOutputHelper output, ListOneTimeProjectionsTests.CustomFixture fixture)
: KurrentTemporaryTests<ListOneTimeProjectionsTests.CustomFixture>(output, fixture) {
[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace Kurrent.Client.Tests;

[Trait("Category", "Target:Projections")]
[Trait("Category", "Target:ProjectionManagement")]
public class ProjectionManagementTests(ITestOutputHelper output, ProjectionManagementTests.CustomFixture fixture)
: KurrentTemporaryTests<ProjectionManagementTests.CustomFixture>(output, fixture) {
[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Kurrent.Client.Tests.Projections;

[Trait("Category", "Target:Projections")]
[Trait("Category", "Target:ProjectionManagement")]
public class ResetProjectionTests(ITestOutputHelper output, ResetProjectionTests.CustomFixture fixture)
: KurrentTemporaryTests<ResetProjectionTests.CustomFixture>(output, fixture) {
[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace Kurrent.Client.Tests.Projections;

[Trait("Category", "Target:Projections")]
[Trait("Category", "Target:ProjectionManagement")]
public class RestartSubsystemTests(ITestOutputHelper output, RestartSubsystemTests.CustomFixture fixture)
: KurrentTemporaryTests<RestartSubsystemTests.CustomFixture>(output, fixture) {
[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Kurrent.Client.Tests.Projections;

[Trait("Category", "Target:Projections")]
[Trait("Category", "Target:ProjectionManagement")]
public class UpdateProjectionTests(ITestOutputHelper output, UpdateProjectionTests.CustomFixture fixture)
: KurrentTemporaryTests<UpdateProjectionTests.CustomFixture>(output, fixture) {
[Theory]
Expand Down
Loading

0 comments on commit eb68a7f

Please sign in to comment.