Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancement: OAuth2TokenProvider for Authentication Against Self-Hosted Camunda Stack (503) #607

Closed
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
9 changes: 9 additions & 0 deletions Client.IntegrationTests/Client.IntegrationTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@
<None Update="Resources\dinnerDecisions.dmn">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Broker\chain.cert.pem">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\Broker\private.key.pem">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Resources\server.crt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

<ItemGroup>
Expand Down
46 changes: 46 additions & 0 deletions Client.IntegrationTests/OAuthIntegrationTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.Threading.Tasks;
using Grpc.Core;
using NUnit.Framework;

namespace Client.IntegrationTests;

[TestFixture]
public class OAuthIntegrationTest
{
private readonly ZeebeIntegrationTestHelper testHelper = ZeebeIntegrationTestHelper.Latest(true);

[OneTimeSetUp]
public async Task Setup()
{
await testHelper.SetupIntegrationTest();
}

[OneTimeTearDown]
public async Task Stop()
{
await testHelper.TearDownIntegrationTest();
}

[Test]
public async Task ShouldSendRequestAndNotFailingWithAuthenticatedClient()
{
var topology = await testHelper.CreateAuthenticatedZeebeClient().TopologyRequest().Send();
var gatewayVersion = topology.GatewayVersion;
Assert.AreEqual(ZeebeIntegrationTestHelper.LatestVersion, gatewayVersion);

var topologyBrokers = topology.Brokers;
Assert.AreEqual(1, topologyBrokers.Count);

var topologyBroker = topologyBrokers[0];
Assert.AreEqual(0, topologyBroker.NodeId);
}

[Test]
public async Task ShouldFailWithUnauthenticatedClient()
{
Assert.ThrowsAsync<RpcException>(code: async () =>
{
await testHelper.CreateZeebeClient().TopologyRequest().Send();
});
}
}
22 changes: 22 additions & 0 deletions Client.IntegrationTests/Resources/Broker/chain.cert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDtDCCApygAwIBAgIUPlH7wI1Bq6F7k63ZNtSXR0DgjxwwDQYJKoZIhvcNAQEL
BQAwVTELMAkGA1UEBhMCREUxEDAOBgNVBAgMB0VuZ2xhbmQxEDAOBgNVBAoMB0Nh
bXVuZGExDjAMBgNVBAsMBXplZWJlMRIwEAYDVQQDDAlsb2NhbGhvc3QwIBcNMjAw
NzA2MTA1MTM3WhgPMjEyMDA2MTIxMDUxMzdaMFUxCzAJBgNVBAYTAkRFMRAwDgYD
VQQIDAdFbmdsYW5kMRAwDgYDVQQKDAdDYW11bmRhMQ4wDAYDVQQLDAV6ZWViZTES
MBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEA0ALY6dhCih7lbyOI79N1HTi7vapsw7DISr+btQgZArsPt/xnU2tTvCH7F7mJ
OQiz1T5cUrNqTi9ZSkP6nDoGsFDZkQRZkJc+fF3OjIUnZ62OGyD7LV62tKisojx4
ulNYW+a7oVu+hQtP2ht3Hsi30fgt9P1Nq+0c11BQzNQfFwo74hFQTVCbYHQf3uU7
W08o0rYCCRIN+rJXUdsD1pm5snFmg7o3nQSUGFYpDHezoZZzL/d4a3YiPAG//YBH
qT/GerhHwa5fK5PFPryns+oq+4PgaF8JB3qVQC6rJWhnmT32n0cQ4OxNTjX5+8JC
jKH3rGkPDSXf3zJaAbU0GWNzEQIDAQABo3oweDAdBgNVHQ4EFgQUgtu9rM/iDT8J
6FvuokdJ3E6gRc4wHwYDVR0jBBgwFoAUgtu9rM/iDT8J6FvuokdJ3E6gRc4wDwYD
VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwFQYDVR0RBA4wDIcEfwAAAYcE
AAAAADANBgkqhkiG9w0BAQsFAAOCAQEAkinfKsHPfDEoOsJ2ic4Bc8ynCV/Fm4GS
huTOxnHiB5KqxN12s8MBd2zTpZNx2H4Pj32W9OUUWluyLvofueTYsarvUHY4TxkT
z04aFFcK5D0lPGLy7eBPSsWmiovCTvjWixxgOiRxYo+t6/ttNvXsZ0PnAZypSbfb
vAb7DeP3SXDEP+QnpIw0PpO3IaoYgilPSrfQV1n4fgFSOVa6545cUpJHINj264qF
Si8d8c1YokLcJFHepUQzTRKOgCgds+e3496iJGhQbhIDw24dMVmefYOqDepTYVlw
KxaCNRcTKljnM9QazGG8scqJaPjGDteDYBzh63+XbdPhjzWK8/yFIA==
-----END CERTIFICATE-----
28 changes: 28 additions & 0 deletions Client.IntegrationTests/Resources/Broker/private.key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDQAtjp2EKKHuVv
I4jv03UdOLu9qmzDsMhKv5u1CBkCuw+3/GdTa1O8IfsXuYk5CLPVPlxSs2pOL1lK
Q/qcOgawUNmRBFmQlz58Xc6MhSdnrY4bIPstXra0qKyiPHi6U1hb5ruhW76FC0/a
G3ceyLfR+C30/U2r7RzXUFDM1B8XCjviEVBNUJtgdB/e5TtbTyjStgIJEg36sldR
2wPWmbmycWaDujedBJQYVikMd7OhlnMv93hrdiI8Ab/9gEepP8Z6uEfBrl8rk8U+
vKez6ir7g+BoXwkHepVALqslaGeZPfafRxDg7E1ONfn7wkKMofesaQ8NJd/fMloB
tTQZY3MRAgMBAAECggEAFnSoPB53mHebZzMb3mAinYP5aJFUao/UH5Wt1o5IPO46
1S7vbKcChCXa+IW0Fa8l0tiHmPn7ePNNnWHXVTRCcphX1Hr2vFBHk5+A49SgG2Y2
GCGoXA6EhN5MvLrwgZTrzggLq3C/EZfWCAK9Clq61XUIaRFLaEsRuQDXqDUiIhdr
5FzoKy/94BhOpqRrF/RQUw8VtUtUSzHaUCBZiFTfBWSKKdYVLNMhu6fsdJOBiXTU
vOpP/IRk4L8vqlZJqRQHu76OcUxd7OA6MsqFjQsYTNvR0y5xsV9T2syKze3cDUPj
oO4fnu/Z2ZvD34/d2lzcjZWjQ5d6vSOYZN2DYBWV0QKBgQDuEySRojaQvPj1aCW5
/mWmgtpCOgScWXgdHagsMWTMEpfqFIBRWeYKyW80eBjRyOHJ+JisHYWFxEXu0ZoX
yqhBpHma0YPsH+Xm778KrbS74aYrW7inIGhYtUX0+n+YQ6q9aroFBq4erTJu/Ev6
4PX1yJPWbHWA3WVi97NWBmmF7QKBgQDfrDtaU9IbIp/LA1HFA1e28iCwDh6S3Rsz
E5K0mEZInfKdGESJfYuAaik0LCmwIaOJ/7fHBx1R7JGTYwt+FeROwBcC6n3qzhbD
K4ptmmTbwzOebz2r1SjX1rMaBIfWkQbxYxecL/HzYSnYF/fEPjCyO8CjXIcum2N4
V9eGFv59NQKBgH7knyIsdq7wujV7XFhlWuLEbfbMm7aGDXpfW0qqzRHkeyod4UL7
Cp0HPomV1YzDaG1RXnamiYuB0NB40YwKzWGne9VkBM+vNMfBU28qpOFbZUlI6wPR
Ryy4+d+YQLf0oSWypBGXvOjG4dG8EfdXPmHRldK9HmggGTEF24Vnh4kFAoGBAIuf
adVy6X8C2BjUU6DV+1U6Q+lihvdKioYRu8x8GbOO1Tn3QiFJe2GH43yr7MID3aBx
PnlBGa5gLGeCtlPYupHmGvc5Ba0jRNZEQb81V6xPZ9OIwUiYYUyKu3aMSXdJRLo+
DyjyTOiOSJ6aJ5Ia+C7qWdAgHEqduTQQMXuEswvZAoGBAN5kC+RjAsEQ5myY7evz
1R/3uk5uwUk/m1GadMCHoWJs7uLdwPa0ThC0Bt9yQxltWseGBj8ysKuWzoy2O11f
gMqZF61NuZibVk+eFc2T9IgPkYXYBSRluafunqEgvclUnuCwUrfwEAN07bHfSuGD
bNGJygSW2596II+xg/TRPhnF
-----END PRIVATE KEY-----
20 changes: 20 additions & 0 deletions Client.IntegrationTests/Resources/server.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDSzCCAjMCFBDDfC44PsguTshkcc2CVESnk/UyMA0GCSqGSIb3DQEBCwUAMGIx
CzAJBgNVBAYTAkRFMRAwDgYDVQQIDAdHZXJtYW55MQ8wDQYDVQQHDAZCZXJsaW4x
ETAPBgNVBAoMCFplZWJlLmlvMQ4wDAYDVQQLDAVaZWViZTENMAsGA1UEAwwEemVs
bDAeFw0xOTA5MDUwNjE5MjFaFw0yOTA5MDIwNjE5MjFaMGIxCzAJBgNVBAYTAkRF
MRAwDgYDVQQIDAdHZXJtYW55MQ8wDQYDVQQHDAZCZXJsaW4xETAPBgNVBAoMCFpl
ZWJlLmlvMQ4wDAYDVQQLDAVaZWViZTENMAsGA1UEAwwEemVsbDCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBAMncS2Y4ddY/7PNu2kW0DYdSvLcv5ONIec0V
E0+Dgls7ElVZJxqOwbIDB8Q4sOT335xpY4rtFK6lUrh541fCkVdtajI+Bv1pzFU+
9LxMPU5GD/uFScVFA2MJy9ps5L6naFYPF3ZkE3s3gb6APTFc5ou5xZTOZ5To0s/Z
ay1RQ92inxjAyvArL9IZSryB6Xq4qyBLUm8wZYZaQ+arLQ8pBADapWkgMY8tKIlA
b185hbb2nY1ns/zws+dsHA5NIk6p7yZ+D3/SXKL/0fDBcPKmgJ+3hkgBpajMtpuw
rLoWyppQfDFGCODiV/Pd1KUrIpbrZorIlkpd3s11fUooqkrtSwMCAwEAATANBgkq
hkiG9w0BAQsFAAOCAQEAmk8VLv7nzOGCvU0gwJI/Sa6JkGdiQ7Jz0cfQGWkqfN6D
Yhw/pFlFelgrD5kGLG8jAGNT4kWiaqGtoFpUkHxADDojJZ63Tk8xUI/o9xFWZkEY
B0pxL39ybzQyuhX/dqkxZdLiNzJV5GTiSCtuTW+N8+WO5CFzT52rxYWQlPP31R5n
p4wzIVqbK/XEYqyyvZyQ5XrM9FIV/57OSXNp5kUXT9RX3HHjp6oaeKOYw6arpcrg
y9LIvuGV4h48ougO0696CgupMgYONKvDI+avRVqxJX/wr0+u56dlQ9+0XhpxCl8G
Sr53Syfz5AD3WMLZt03iO4IfW5MVnq5LRLhPCVqiQg==
-----END CERTIFICATE-----
184 changes: 166 additions & 18 deletions Client.IntegrationTests/ZeebeIntegrationTestHelper.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
using System;
using System.IO;
using System.Threading;
using System.Net;
using System.Threading.Tasks;
using DotNet.Testcontainers.Builders;
using DotNet.Testcontainers.Configurations;
using DotNet.Testcontainers.Containers;
using DotNet.Testcontainers.Images;
using DotNet.Testcontainers.Networks;
using Microsoft.Extensions.Logging;
using NLog.Extensions.Logging;
using Zeebe.Client;
using Zeebe.Client.Impl.Builder;
using IContainer = DotNet.Testcontainers.Containers.IContainer;

namespace Client.IntegrationTests
{
Expand All @@ -18,22 +20,30 @@ public class ZeebeIntegrationTestHelper

private const ushort ZeebePort = 26500;

private IContainer container;

private IContainer zeebeContainer;
private IZeebeClient client;

private readonly string version;
private readonly string audience;
private readonly bool withIdentity;
private int count = 1;
public readonly ILoggerFactory LoggerFactory;
private IContainer postgresContainer;
private IContainer keycloakContainer;
private IContainer identityContainer;

private ZeebeIntegrationTestHelper(string version)
private ZeebeIntegrationTestHelper(string version, bool withIdentity = false)
{
this.version = version;
this.withIdentity = withIdentity;
audience = Guid.NewGuid().ToString();
LoggerFactory = new NLogLoggerFactory();
}

public static ZeebeIntegrationTestHelper Latest()
public static ZeebeIntegrationTestHelper Latest(bool withIdentity = false)
{
return new ZeebeIntegrationTestHelper(LatestVersion);
return new ZeebeIntegrationTestHelper(LatestVersion, withIdentity);
}

public ZeebeIntegrationTestHelper WithPartitionCount(int count)
Expand All @@ -50,10 +60,38 @@ public static ZeebeIntegrationTestHelper OfVersion(string version)
public async Task<IZeebeClient> SetupIntegrationTest()
{
TestcontainersSettings.Logger = LoggerFactory.CreateLogger<ZeebeIntegrationTestHelper>();
container = CreateZeebeContainer();
await container.StartAsync();

client = CreateZeebeClient();
if (withIdentity)
{
var network = new NetworkBuilder()
.WithName(Guid.NewGuid().ToString("D"))
.Build();

postgresContainer = CreatePostgresContainer(network);
await postgresContainer.StartAsync();
keycloakContainer = CreateKeyCloakContainer(network);
await keycloakContainer.StartAsync();

identityContainer = CreateIdentityContainer(network);
await identityContainer.StartAsync();
zeebeContainer = CreateZeebeContainer(true, network);
}
else
{
zeebeContainer = CreateZeebeContainer(false);
}

await zeebeContainer.StartAsync();

if (withIdentity)
{
client = CreateAuthenticatedZeebeClient();
}
else
{
client = CreateZeebeClient();
}

await AwaitBrokerReadiness();
return client;
}
Expand All @@ -62,24 +100,119 @@ public async Task TearDownIntegrationTest()
{
client.Dispose();
client = null;
await container.StopAsync();
container = null;
if (withIdentity)
{
await postgresContainer.StopAsync();
postgresContainer = null;
await keycloakContainer.StopAsync();
keycloakContainer = null;
await identityContainer.StopAsync();
identityContainer = null;
}

await zeebeContainer.StopAsync();
zeebeContainer = null;
}

private IContainer CreateZeebeContainer()
private IContainer CreateZeebeContainer(bool withKeycloak, INetwork network = null)
{
return new ContainerBuilder()
var containerBuilder = new ContainerBuilder()
.WithImage(new DockerImage("camunda", "zeebe", version))
.WithPortBinding(ZeebePort, true)
.WithEnvironment("ZEEBE_BROKER_CLUSTER_PARTITIONSCOUNT", count.ToString())
.WithEnvironment("ZEEBE_BROKER_CLUSTER_PARTITIONSCOUNT", count.ToString());

if (withKeycloak)
{
containerBuilder = containerBuilder.WithEnvironment("ZEEBE_BROKER_GATEWAY_SECURITY_AUTHENTICATION_MODE",
"identity")
.WithEnvironment(
"ZEEBE_BROKER_GATEWAY_SECURITY_AUTHENTICATION_IDENTITY_ISSUERBACKENDURL",
"http://integration-keycloak:8080/auth/realms/camunda-platform")
.WithEnvironment("ZEEBE_BROKER_GATEWAY_SECURITY_AUTHENTICATION_IDENTITY_AUDIENCE",
"zeebe-api")
.WithEnvironment("ZEEBE_BROKER_GATEWAY_SECURITY_ENABLED", "true")
.WithEnvironment("ZEEBE_BROKER_GATEWAY_SECURITY_CERTIFICATECHAINPATH", "/security/chain.cert.pem")
.WithEnvironment("ZEEBE_BROKER_GATEWAY_SECURITY_PRIVATEKEYPATH", "/security/private.key.pem")
.WithResourceMapping(new DirectoryInfo("./Resources/Broker"), "/security")
.WithNetwork(network);
}

containerBuilder = containerBuilder.WithAutoRemove(true);
return containerBuilder.Build();
}

private IContainer CreatePostgresContainer(INetwork network)
{
var containerBuilder = new ContainerBuilder()
.WithImage("postgres")
.WithName("integration-postgres")
.WithPortBinding(5432, true)
.WithEnvironment("POSTGRES_DB", "bitnami_keycloak")
.WithEnvironment("POSTGRES_USER", "bn_keycloak")
.WithEnvironment("POSTGRES_PASSWORD", "#3]O?4RGj)DE7Z!9SA5")
.WithNetwork(network)
.WithAutoRemove(true)
.Build();
.WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(5432));

return containerBuilder.Build();
}

private IContainer CreateIdentityContainer(INetwork network)
{
var containerBuilder = new ContainerBuilder()
.WithImage(new DockerImage("camunda", "identity", "8.3.0"))
.WithName("integration-identity")
.WithExposedPort("8084")
.WithPortBinding("8084", "8084")
.WithEnvironment("SERVER_PORT", "8084")
.WithEnvironment("IDENTITY_RETRY_DELAY_SECONDS", "30")
.WithEnvironment("KEYCLOAK_URL", "http://integration-keycloak:8080/auth")
.WithEnvironment("IDENTITY_AUTH_PROVIDER_BACKEND_URL",
"http://integration-keycloak:8080/auth/realms/camunda-platform")
.WithEnvironment("IDENTITY_DATABASE_HOST", "integration-postgres")
.WithEnvironment("IDENTITY_DATABASE_PORT", "5432")
.WithEnvironment("IDENTITY_DATABASE_NAME", "bitnami_keycloak")
.WithEnvironment("IDENTITY_DATABASE_USERNAME", "bn_keycloak")
.WithEnvironment("IDENTITY_DATABASE_PASSWORD", "#3]O?4RGj)DE7Z!9SA5")
.WithEnvironment("KEYCLOAK_INIT_ZEEBE_NAME", "zeebe")
.WithEnvironment("KEYCLOAK_CLIENTS_0_NAME", "zeebe")
.WithEnvironment("KEYCLOAK_CLIENTS_0_ID", "zeebe")
.WithEnvironment("KEYCLOAK_CLIENTS_0_SECRET", "sddh123865WUS)(1%!")
.WithEnvironment("KEYCLOAK_CLIENTS_0_TYPE", "M2M")
.WithEnvironment("KEYCLOAK_CLIENTS_0_PERMISSIONS_0_RESOURCE_SERVER_ID", "zeebe-api")
.WithEnvironment("KEYCLOAK_CLIENTS_0_PERMISSIONS_0_DEFINITION", "write:*")
.WithEnvironment("RESOURCE_PERMISSIONS_ENABLED", "false")
.WithAutoRemove(true)
.WithNetwork(network);


return containerBuilder.Build();
}

private IZeebeClient CreateZeebeClient()
private IContainer CreateKeyCloakContainer(INetwork network)
{
var containerBuilder = new ContainerBuilder()
.WithImage(new DockerImage("bitnami", "keycloak", "21.1.2"))
.WithName("integration-keycloak")
.WithPortBinding("18080", "8080")
.WithEnvironment("KEYCLOAK_HTTP_RELATIVE_PATH", "/auth")
.WithEnvironment("KEYCLOAK_DATABASE_HOST", "integration-postgres")
.WithEnvironment("KEYCLOAK_DATABASE_PASSWORD", "#3]O?4RGj)DE7Z!9SA5")
.WithEnvironment("KEYCLOAK_ADMIN_USER", "admin")
.WithEnvironment("KEYCLOAK_ADMIN_PASSWORD", "admin")
.WithNetwork(network)
.WithAutoRemove(true)
.WithWaitStrategy(Wait.ForUnixContainer().UntilHttpRequestIsSucceeded(request =>
request.ForPort(8080).ForPath("/auth").ForStatusCode(HttpStatusCode.OK)));


return containerBuilder.Build();
}

public IZeebeClient CreateZeebeClient()
{
var loggerFactory = LoggerFactory;
var host = container.Hostname + ":" + container.GetMappedPublicPort(ZeebePort);
var host = zeebeContainer.Hostname + ":" + zeebeContainer.GetMappedPublicPort(ZeebePort);

return ZeebeClient.Builder()
.UseLoggerFactory(loggerFactory)
Expand All @@ -88,9 +221,24 @@ private IZeebeClient CreateZeebeClient()
.Build();
}

public IZeebeClient CreateAuthenticatedZeebeClient()
{
var loggerFactory = LoggerFactory;
var host = zeebeContainer.Hostname + ":" + zeebeContainer.GetMappedPublicPort(ZeebePort);

return ZeebeClient.Builder()
.UseLoggerFactory(loggerFactory)
.UseGatewayAddress(host)
.UseTransportEncryption()
.AllowUntrustedCertificates()
.UseAccessTokenSupplier(new OAuth2TokenProvider(
$"http://{keycloakContainer.Hostname}:{keycloakContainer.GetMappedPublicPort(8080)}/auth/realms/camunda-platform/protocol/openid-connect/token",
"zeebe", "sddh123865WUS)(1%!", audience)).Build();
}

private async Task AwaitBrokerReadiness()
{
var zeebeClient = (ZeebeClient) client;
var zeebeClient = withIdentity ? (ZeebeClient)CreateAuthenticatedZeebeClient() : (ZeebeClient)client;
await zeebeClient.Connect();
var topologyErrorLogger = LoggerFactory.CreateLogger<ZeebeIntegrationTestHelper>();
var ready = false;
Expand Down
Loading