Skip to content

Commit

Permalink
Refactor and add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
w1am committed Apr 3, 2024
1 parent 627b998 commit 27fb10d
Show file tree
Hide file tree
Showing 11 changed files with 218 additions and 22 deletions.
65 changes: 65 additions & 0 deletions .github/workflows/ee.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: Test EE

on:
pull_request:
push:
branches:
- master
tags:
- v*

env:
ES_VERSION: 24.2.0-jammy
ES_DOCKER_REGISTRY: docker.eventstore.com/eventstore-ee/eventstoredb-commercial

jobs:
test:
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
framework: [ net6.0, net7.0, net8.0 ]
os: [ ubuntu-latest ]
build: [ Streams, PersistentSubscriptions, Operations, UserManagement, ProjectionManagement ]
test: [ Plugins ]
configuration: [ release ]
runs-on: ${{ matrix.os }}
name: EventStore.Client.${{ matrix.test }}/${{ matrix.os }}/${{ matrix.framework }}/${{ env.ES_VERSION }}
steps:
- name: Checkout
uses: actions/checkout@v3
- shell: bash
run: |
git fetch --prune --unshallow
- name: Login to Cloudsmith
uses: docker/login-action@v3
with:
registry: docker.eventstore.com
username: ${{ secrets.CLOUDSMITH_CICD_USER }}
password: ${{ secrets.CLOUDSMITH_CICD_TOKEN }}
- name: Pull EventStore Image
shell: bash
run: |
docker pull ${{ env.ES_DOCKER_REGISTRY }}:${{ env.ES_VERSION }}
- name: Install dotnet SDKs
uses: actions/setup-dotnet@v3
with:
dotnet-version: |
6.0.x
7.0.x
8.0.x
- name: Compile
shell: bash
run: |
dotnet build --configuration ${{ matrix.configuration }} --framework ${{ matrix.framework }} src/EventStore.Client.${{ matrix.build }}
- name: Run Tests
shell: bash
env:
ES_DOCKER_TAG: ${{ env.ES_VERSION }}
ES_DOCKER_REGISTRY: ${{ env.ES_DOCKER_REGISTRY }}
run: |
sudo ./gencert.sh
dotnet test --configuration ${{ matrix.configuration }} --blame \
--logger:"GitHubActions;report-warnings=false" --logger:"console;verbosity=normal" \
--framework ${{ matrix.framework }} \
test/EventStore.Client.${{ matrix.test }}.Tests
7 changes: 7 additions & 0 deletions EventStore.Client.sln
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventStore.Client.UserManag
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventStore.Client.Tests.Common", "test\EventStore.Client.Tests.Common\EventStore.Client.Tests.Common.csproj", "{E326832D-DE52-4DE4-9E54-C800908B75F3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventStore.Client.Plugins.Tests", "test\EventStore.Client.Plugins.Tests\EventStore.Client.Plugins.Tests.csproj", "{7D929D45-F1D9-462B-BE49-84BEC11D5039}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Expand Down Expand Up @@ -94,6 +96,10 @@ Global
{E326832D-DE52-4DE4-9E54-C800908B75F3}.Debug|x64.Build.0 = Debug|Any CPU
{E326832D-DE52-4DE4-9E54-C800908B75F3}.Release|x64.ActiveCfg = Release|Any CPU
{E326832D-DE52-4DE4-9E54-C800908B75F3}.Release|x64.Build.0 = Release|Any CPU
{7D929D45-F1D9-462B-BE49-84BEC11D5039}.Debug|x64.ActiveCfg = Debug|Any CPU
{7D929D45-F1D9-462B-BE49-84BEC11D5039}.Debug|x64.Build.0 = Debug|Any CPU
{7D929D45-F1D9-462B-BE49-84BEC11D5039}.Release|x64.ActiveCfg = Release|Any CPU
{7D929D45-F1D9-462B-BE49-84BEC11D5039}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{D3744A86-DD35-4104-AAEE-84B79062C4A2} = {EA59C1CB-16DA-4F68-AF8A-642A969B4CF8}
Expand All @@ -109,5 +115,6 @@ Global
{6CEB731F-72E1-461F-A6B3-54DBF3FD786C} = {C51F2C69-45A9-4D0D-A708-4FC319D5D340}
{22634CEE-4F7B-4679-A48D-38A2A8580ECA} = {C51F2C69-45A9-4D0D-A708-4FC319D5D340}
{E326832D-DE52-4DE4-9E54-C800908B75F3} = {C51F2C69-45A9-4D0D-A708-4FC319D5D340}
{7D929D45-F1D9-462B-BE49-84BEC11D5039} = {C51F2C69-45A9-4D0D-A708-4FC319D5D340}
EndGlobalSection
EndGlobal
18 changes: 11 additions & 7 deletions gencert.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,22 @@ Write-Host ">> Generating certificate..."
New-Item -ItemType Directory -Path .\certs -Force

# Set permissions for the directory
icacls .\certs /grant:r "$($env:UserName):(OI)(CI)RX"
icacls .\certs /grant:r "$($env:UserName):(OI)(CI)F"

# Pull the Docker image
docker pull eventstore/es-gencert-cli:1.0.2
docker pull ghcr.io/eventstore/es-gencert-cli:1.3

# Create CA certificate
docker run --rm --volume ${PWD}\certs:/tmp --user (Get-Process -Id $PID).SessionId eventstore/es-gencert-cli:1.0.2 create-ca -out /tmp/ca
docker run --rm --volume ${PWD}\certs:/tmp ghcr.io/eventstore/es-gencert-cli create-ca -out /tmp/ca

# Create node certificate
docker run --rm --volume ${PWD}\certs:/tmp --user (Get-Process -Id $PID).SessionId eventstore/es-gencert-cli:1.0.2 create-node -ca-certificate /tmp/ca/ca.crt -ca-key /tmp/ca/ca.key -out /tmp/node -ip-addresses 127.0.0.1 -dns-names localhost
docker run --rm --volume ${PWD}\certs:/tmp ghcr.io/eventstore/es-gencert-cli create-node -ca-certificate /tmp/ca/ca.crt -ca-key /tmp/ca/ca.key -out /tmp/node -ip-addresses 127.0.0.1 -dns-names localhost

# Create admin user
docker run --rm --volume ${PWD}\certs:/tmp ghcr.io/eventstore/es-gencert-cli create-user -username admin -ca-certificate /tmp/ca/ca.crt -ca-key /tmp/ca/ca.key -out /tmp/user-admin

# Create an invalid user
docker run --rm --volume ${PWD}\certs:/tmp ghcr.io/eventstore/es-gencert-cli create-user -username invalid -ca-certificate /tmp/ca/ca.crt -ca-key /tmp/ca/ca.key -out /tmp/user-invalid

# Set permissions recursively for the directory
icacls .\certs /grant:r "$($env:UserName):(OI)(CI)RX"
icacls .\certs /grant:r "$($env:UserName):(OI)(CI)F"

Import-Certificate -FilePath ".\certs\ca\ca.crt" -CertStoreLocation Cert:\CurrentUser\Root
10 changes: 7 additions & 3 deletions gencert.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,15 @@ mkdir -p certs

chmod 0755 ./certs

docker pull eventstore/es-gencert-cli:1.0.2
docker pull ghcr.io/eventstore/es-gencert-cli:1.3

docker run --rm --volume $PWD/certs:/tmp --user $(id -u):$(id -g) eventstore/es-gencert-cli:1.0.2 create-ca -out /tmp/ca
docker run --rm --volume $PWD/certs:/tmp --user $(id -u):$(id -g) ghcr.io/eventstore/es-gencert-cli create-ca -out /tmp/ca

docker run --rm --volume $PWD/certs:/tmp --user $(id -u):$(id -g) eventstore/es-gencert-cli:1.0.2 create-node -ca-certificate /tmp/ca/ca.crt -ca-key /tmp/ca/ca.key -out /tmp/node -ip-addresses 127.0.0.1 -dns-names localhost
docker run --rm --volume $PWD/certs:/tmp --user $(id -u):$(id -g) ghcr.io/eventstore/es-gencert-cli create-node -ca-certificate /tmp/ca/ca.crt -ca-key /tmp/ca/ca.key -out /tmp/node -ip-addresses 127.0.0.1 -dns-names localhost

docker run --rm --volume $PWD/certs:/tmp --user $(id -u):$(id -g) ghcr.io/eventstore/es-gencert-cli create-user -username admin -ca-certificate /tmp/ca/ca.crt -ca-key /tmp/ca/ca.key -out /tmp/user-admin

docker run --rm --volume $PWD/certs:/tmp --user $(id -u):$(id -g) ghcr.io/eventstore/es-gencert-cli create-user -username invalid -ca-certificate /tmp/ca/ca.crt -ca-key /tmp/ca/ca.key -out /tmp/user-invalid

chmod -R 0755 ./certs

Expand Down
7 changes: 3 additions & 4 deletions src/EventStore.Client/ChannelFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ HttpMessageHandler CreateHandler() {
return settings.CreateHttpMessageHandler.Invoke();
}

var configureClientCert = settings.ConnectivitySettings is { TlsCaFile: not null, Insecure: false };

var certificate = settings.ConnectivitySettings.ClientCertificate ??
settings.ConnectivitySettings.TlsCaFile;

var configureClientCert = settings.ConnectivitySettings is { Insecure: false } && certificate != null;
#if NET
var handler = new SocketsHttpHandler {
KeepAlivePingDelay = settings.ConnectivitySettings.KeepAliveInterval,
Expand All @@ -57,8 +57,7 @@ HttpMessageHandler CreateHandler() {
EnableMultipleHttp2Connections = true
};
#endif
if (!settings.ConnectivitySettings.Insecure) return handler;

if (settings.ConnectivitySettings.Insecure) return handler;
#if NET
if (configureClientCert) {
handler.SslOptions.ClientCertificates = new X509CertificateCollection { certificate! };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public bool Insecure {
/// <summary>
/// Client certificate used for user authentication.
/// </summary>
public X509Certificate2? ClientCertificate { get; set; } = null;
public X509Certificate2? ClientCertificate { get; set; }

/// <summary>
/// The default <see cref="EventStoreClientConnectivitySettings"/>.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,11 +249,11 @@ private static EventStoreClientSettings CreateSettings(
return settings;

HttpMessageHandler CreateDefaultHandler() {
var configureClientCert = settings.ConnectivitySettings is { TlsCaFile: not null, Insecure: false };

var certificate = settings.ConnectivitySettings.ClientCertificate ??
settings.ConnectivitySettings.TlsCaFile;

var configureClientCert = settings.ConnectivitySettings is { Insecure: false } && certificate != null;

#if NET
var handler = new SocketsHttpHandler {
KeepAlivePingDelay = settings.ConnectivitySettings.KeepAliveInterval,
Expand All @@ -268,7 +268,7 @@ HttpMessageHandler CreateDefaultHandler() {
EnableMultipleHttp2Connections = true
};
#endif

if (settings.ConnectivitySettings.Insecure) return handler;
#if NET
if (configureClientCert) {
handler.SslOptions.ClientCertificates = [certificate!];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\EventStore.Client.Tests.Common\EventStore.Client.Tests.Common.csproj"/>
</ItemGroup>
</Project>
88 changes: 88 additions & 0 deletions test/EventStore.Client.Plugins.Tests/client_certificate.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
namespace EventStore.Client.Plugins.Tests;

[Trait("Category", "Target:Plugins")]
[Trait("Category", "Type:UserCertificate")]
public class client_certificate(ITestOutputHelper output, EventStoreFixture fixture)
: EventStoreTests<EventStoreFixture>(output, fixture) {
public static IEnumerable<object[]> TlsCertPaths =>
new List<object[]> {
new object[] { Path.Combine("certs", "ca", "ca.crt") },
new object[] { Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "certs", "ca", "ca.crt") },
};

public static IEnumerable<object[]> AdminClientCertPaths =>
new List<object[]> {
new object[] {
Path.Combine(Environment.CurrentDirectory, "certs", "user-admin", "user-admin.crt"),
Path.Combine(Environment.CurrentDirectory, "certs", "user-admin", "user-admin.key")
},
new object[] {
Path.Combine("certs", "user-admin", "user-admin.crt"),
Path.Combine("certs", "user-admin", "user-admin.key")
}
};

public static IEnumerable<object[]> BadClientCertPaths =>
new List<object[]> {
new object[] {
Path.Combine("certs", "user-invalid", "user-invalid.crt"),
Path.Combine("certs", "user-invalid", "user-invalid.key")
},
new object[] {
Path.Combine(Environment.CurrentDirectory, "certs", "user-invalid", "user-invalid.crt"),
Path.Combine(Environment.CurrentDirectory, "certs", "user-invalid", "user-invalid.key")
}
};

[Theory]
[MemberData(nameof(TlsCertPaths))]
private async Task append_with_different_tls_cert_path(string certificateFilePath) {
await AppendWithCertificate($"esdb://admin:changeit@localhost:2113/?tls=true&tlsVerifyCert=true&tlsCAFile={certificateFilePath}");
}

[Theory]
[MemberData(nameof(AdminClientCertPaths))]
private async Task append_with_admin_client_certificate(string certPath, string certKeyPath) {
await AppendWithCertificate($"esdb://localhost:2113/?tls=true&tlsVerifyCert=true&certPath={certPath}&certKeyPath={certKeyPath}");
}

[Theory]
[MemberData(nameof(BadClientCertPaths))]
private async Task append_with_bad_client_certificate(string certPath, string certKeyPath) {
await AssertAppendFailsWithCertificate($"esdb://localhost:2113/?tls=true&tlsVerifyCert=true&certPath={certPath}&certKeyPath={certKeyPath}", typeof(NotAuthenticatedException));
}

[Theory]
[MemberData(nameof(BadClientCertPaths))]
private async Task user_credentials_takes_precedence_over_client_certificates(string certPath, string certKeyPath) {
await AppendWithCertificate($"esdb://admin:changeit@localhost:2113/?tls=true&tlsVerifyCert=true&certPath={certPath}&certKeyPath={certKeyPath}");
}

private async Task AppendWithCertificate(string connectionString) {
var settings = EventStoreClientSettings.Create(connectionString);
var client = new EventStoreClient(settings);

var appendResult = await client.AppendToStreamAsync(
Fixture.GetStreamName(),
StreamState.Any,
Fixture.CreateTestEvents(1)
);

appendResult.ShouldNotBeNull();

await client.DisposeAsync();
}

private async Task AssertAppendFailsWithCertificate(string connectionString, Type expectedExceptionType) {
var settings = EventStoreClientSettings.Create(connectionString);
var client = new EventStoreClient(settings);

await client.AppendToStreamAsync(
Fixture.GetStreamName(),
StreamState.Any,
Fixture.CreateTestEvents(1)
).ShouldThrowAsync(expectedExceptionType);

await client.DisposeAsync();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,12 @@ public static EventStoreFixtureOptions DefaultOptions() {
["EVENTSTORE_DISABLE_LOG_FILE"] = "true",
["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";
}

// TODO SS: must find a way to enable parallel tests on CI. It works locally.
if (port != NetworkPortProvider.DefaultEsdbPort) {
if (GlobalEnvironment.Variables.TryGetValue("ES_DOCKER_TAG", out var tag) && tag == "ci")
Expand Down
24 changes: 21 additions & 3 deletions test/EventStore.Client.Tests/ConnectionStringTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,21 +148,39 @@ public void tls_verify_cert(bool tlsVerifyCert) {

#endif

public static IEnumerable<object?[]> InvalidClientCertificates() {
public static IEnumerable<object?[]> InvalidTlsCertificates() {
yield return new object?[] { Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "path", "not", "found") };
yield return new object?[] { Assembly.GetExecutingAssembly().Location };
}

[Theory]
[MemberData(nameof(InvalidClientCertificates))]
public void connection_string_with_invalid_client_certificate_should_throw(string clientCertificatePath) {
[MemberData(nameof(InvalidTlsCertificates))]
public void connection_string_with_invalid_tls_certificate_should_throw(string clientCertificatePath) {
Assert.Throws<InvalidClientCertificateException >(
() => EventStoreClientSettings.Create(
$"esdb://admin:changeit@localhost:2113/?tls=true&tlsVerifyCert=true&tlsCAFile={clientCertificatePath}"
)
);
}

public static IEnumerable<object?[]> InvalidClientCertificates() {
var invalidPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "path", "not", "found");
yield return [invalidPath, null];
yield return [null, invalidPath];
yield return [null, null];
yield return [invalidPath, invalidPath];
}

[Theory]
[MemberData(nameof(InvalidClientCertificates))]
public void connection_string_with_invalid_client_certificate_should_throw(string certPath, string certKeyPath) {
Assert.Throws<InvalidClientCertificateException >(
() => EventStoreClientSettings.Create(
$"esdb://admin:changeit@localhost:2113/?tls=true&tlsVerifyCert=true&certPath={certPath}&certKeyPath={certKeyPath}"
)
);
}

[Fact]
public void infinite_grpc_timeouts() {
var result = EventStoreClientSettings.Create("esdb://localhost:2113?keepAliveInterval=-1&keepAliveTimeout=-1");
Expand Down

0 comments on commit 27fb10d

Please sign in to comment.