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

Release 3.1 #12

Merged
merged 19 commits into from
Jun 3, 2024
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
Binary file not shown.
Binary file removed .github/images/AzureApp-basic-store-type-dialog.png
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file removed .github/images/AzureSP-basic-store-type-dialog.png
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

Expand Down
75 changes: 54 additions & 21 deletions AzureEnterpriseApplicationOrchestrator.Tests/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,33 @@
ConfigureLogging();
}

[IntegrationTestingFact]
public void GraphClient_Application_AddGetRemove_ReturnSuccess()
[IntegrationTestingTheory]
[InlineData("clientcert")]
[InlineData("clientsecret")]
public void GraphClient_Application_AddGetRemove_ReturnSuccess(string testAuthMethod)
{
// Arrange
string certName = "AppTest" + Guid.NewGuid().ToString()[..6];
X509Certificate2 ssCert = GetSelfSignedCert(certName);
string b64Cert = Convert.ToBase64String(ssCert.Export(X509ContentType.Cert));

IntegrationTestingFact env = new();

IAzureGraphClient client = new GraphClient.Builder()
IAzureGraphClientBuilder clientBuilder = new GraphClient.Builder()
.WithTenantId(env.TenantId)
.WithApplicationId(env.ApplicationId)
.WithClientSecret(env.ClientSecret)
.WithTargetApplicationId(env.TargetApplicationId)
.Build();
.WithTargetApplicationId(env.TargetApplicationId);

if (testAuthMethod == "clientcert")
{
clientBuilder.WithClientSecret(env.ClientSecret);
}
else
{
var cert = X509Certificate2.CreateFromPemFile(env.ClientCertificatePath);
clientBuilder.WithClientCertificate(cert);
}

IAzureGraphClient client = clientBuilder.Build();

// Step 1 - Add the certificate to the Application

Expand All @@ -61,8 +72,8 @@
// Assert
Assert.True(operationResult.Success);
Assert.NotNull(operationResult.Result);
Assert.True(operationResult.Result.Any(c => c.Alias == certName));

Check warning on line 75 in AzureEnterpriseApplicationOrchestrator.Tests/Client.cs

View workflow job for this annotation

GitHub Actions / Build and Test dotnet project

Do not use Enumerable.Any() to check if a value exists in a collection. (https://xunit.github.io/xunit.analyzers/rules/xUnit2012)

Check warning on line 75 in AzureEnterpriseApplicationOrchestrator.Tests/Client.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

Do not use Enumerable.Any() to check if a value exists in a collection. (https://xunit.github.io/xunit.analyzers/rules/xUnit2012)

Check warning on line 75 in AzureEnterpriseApplicationOrchestrator.Tests/Client.cs

View workflow job for this annotation

GitHub Actions / Build and Test dotnet project

Do not use Enumerable.Any() to check if a value exists in a collection. (https://xunit.github.io/xunit.analyzers/rules/xUnit2012)

Check warning on line 75 in AzureEnterpriseApplicationOrchestrator.Tests/Client.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

Do not use Enumerable.Any() to check if a value exists in a collection. (https://xunit.github.io/xunit.analyzers/rules/xUnit2012)

Check warning on line 75 in AzureEnterpriseApplicationOrchestrator.Tests/Client.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

Do not use Enumerable.Any() to check if a value exists in a collection. (https://xunit.github.io/xunit.analyzers/rules/xUnit2012)
Assert.True(operationResult.Result.Any(c => c.Alias == certName && c.PrivateKeyEntry == false));

Check warning on line 76 in AzureEnterpriseApplicationOrchestrator.Tests/Client.cs

View workflow job for this annotation

GitHub Actions / Build and Test dotnet project

Do not use Enumerable.Any() to check if a value exists in a collection. (https://xunit.github.io/xunit.analyzers/rules/xUnit2012)

Check warning on line 76 in AzureEnterpriseApplicationOrchestrator.Tests/Client.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

Do not use Enumerable.Any() to check if a value exists in a collection. (https://xunit.github.io/xunit.analyzers/rules/xUnit2012)

Check warning on line 76 in AzureEnterpriseApplicationOrchestrator.Tests/Client.cs

View workflow job for this annotation

GitHub Actions / Build and Test dotnet project

Do not use Enumerable.Any() to check if a value exists in a collection. (https://xunit.github.io/xunit.analyzers/rules/xUnit2012)

Check warning on line 76 in AzureEnterpriseApplicationOrchestrator.Tests/Client.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

Do not use Enumerable.Any() to check if a value exists in a collection. (https://xunit.github.io/xunit.analyzers/rules/xUnit2012)

Check warning on line 76 in AzureEnterpriseApplicationOrchestrator.Tests/Client.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

Do not use Enumerable.Any() to check if a value exists in a collection. (https://xunit.github.io/xunit.analyzers/rules/xUnit2012)

// Step 3 - Determine if the certificate exists in the Application

Expand All @@ -89,8 +100,10 @@
Assert.False(exists);
}

[IntegrationTestingFact]
public void GraphClient_ServicePrincipal_AddGetRemove_ReturnSuccess()
[IntegrationTestingTheory]
[InlineData("clientcert")]
[InlineData("clientsecret")]
public void GraphClient_ServicePrincipal_AddGetRemove_ReturnSuccess(string testAuthMethod)
{
// Arrange
const string password = "passwordpasswordpassword";
Expand All @@ -99,13 +112,22 @@
string b64PfxSslCert = Convert.ToBase64String(ssCert.Export(X509ContentType.Pfx, password));

IntegrationTestingFact env = new();

IAzureGraphClient client = new GraphClient.Builder()
IAzureGraphClientBuilder clientBuilder = new GraphClient.Builder()
.WithTenantId(env.TenantId)
.WithApplicationId(env.ApplicationId)
.WithClientSecret(env.ClientSecret)
.WithTargetApplicationId(env.TargetApplicationId)
.Build();
.WithTargetApplicationId(env.TargetApplicationId);

if (testAuthMethod == "clientcert")
{
clientBuilder.WithClientSecret(env.ClientSecret);
}
else
{
var cert = X509Certificate2.CreateFromPemFile(env.ClientCertificatePath);
clientBuilder.WithClientCertificate(cert);
}

IAzureGraphClient client = clientBuilder.Build();

// Step 1 - Add the certificate to the Service Principal (and set it as the preferred SAML signing certificate)

Expand All @@ -121,8 +143,8 @@
// Assert
Assert.True(operationResult.Success);
Assert.NotNull(operationResult.Result);
Assert.True(operationResult.Result.Any(c => c.Alias == certName));

Check warning on line 146 in AzureEnterpriseApplicationOrchestrator.Tests/Client.cs

View workflow job for this annotation

GitHub Actions / Build and Test dotnet project

Do not use Enumerable.Any() to check if a value exists in a collection. (https://xunit.github.io/xunit.analyzers/rules/xUnit2012)

Check warning on line 146 in AzureEnterpriseApplicationOrchestrator.Tests/Client.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

Do not use Enumerable.Any() to check if a value exists in a collection. (https://xunit.github.io/xunit.analyzers/rules/xUnit2012)

Check warning on line 146 in AzureEnterpriseApplicationOrchestrator.Tests/Client.cs

View workflow job for this annotation

GitHub Actions / Build and Test dotnet project

Do not use Enumerable.Any() to check if a value exists in a collection. (https://xunit.github.io/xunit.analyzers/rules/xUnit2012)

Check warning on line 146 in AzureEnterpriseApplicationOrchestrator.Tests/Client.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

Do not use Enumerable.Any() to check if a value exists in a collection. (https://xunit.github.io/xunit.analyzers/rules/xUnit2012)

Check warning on line 146 in AzureEnterpriseApplicationOrchestrator.Tests/Client.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

Do not use Enumerable.Any() to check if a value exists in a collection. (https://xunit.github.io/xunit.analyzers/rules/xUnit2012)
Assert.True(operationResult.Result.Any(c => c.Alias == certName && c.PrivateKeyEntry));

Check warning on line 147 in AzureEnterpriseApplicationOrchestrator.Tests/Client.cs

View workflow job for this annotation

GitHub Actions / Build and Test dotnet project

Do not use Enumerable.Any() to check if a value exists in a collection. (https://xunit.github.io/xunit.analyzers/rules/xUnit2012)

Check warning on line 147 in AzureEnterpriseApplicationOrchestrator.Tests/Client.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

Do not use Enumerable.Any() to check if a value exists in a collection. (https://xunit.github.io/xunit.analyzers/rules/xUnit2012)

Check warning on line 147 in AzureEnterpriseApplicationOrchestrator.Tests/Client.cs

View workflow job for this annotation

GitHub Actions / Build and Test dotnet project

Do not use Enumerable.Any() to check if a value exists in a collection. (https://xunit.github.io/xunit.analyzers/rules/xUnit2012)

Check warning on line 147 in AzureEnterpriseApplicationOrchestrator.Tests/Client.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

Do not use Enumerable.Any() to check if a value exists in a collection. (https://xunit.github.io/xunit.analyzers/rules/xUnit2012)

Check warning on line 147 in AzureEnterpriseApplicationOrchestrator.Tests/Client.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

Do not use Enumerable.Any() to check if a value exists in a collection. (https://xunit.github.io/xunit.analyzers/rules/xUnit2012)

// Step 3 - Determine if the certificate exists in the Service Principal

Expand Down Expand Up @@ -152,8 +174,10 @@
Assert.False(exists);
}

[IntegrationTestingFact]
public void GraphClient_DiscoverApplicationIds_ReturnSuccess()
[IntegrationTestingTheory]
[InlineData("clientcert")]
[InlineData("clientsecret")]
public void GraphClient_DiscoverApplicationIds_ReturnSuccess(string testAuthMethod)
{
// Arrange
const string password = "passwordpasswordpassword";
Expand All @@ -162,13 +186,22 @@
string b64PfxSslCert = Convert.ToBase64String(ssCert.Export(X509ContentType.Pfx, password));

IntegrationTestingFact env = new();

IAzureGraphClient client = new GraphClient.Builder()
IAzureGraphClientBuilder clientBuilder = new GraphClient.Builder()
.WithTenantId(env.TenantId)
.WithApplicationId(env.ApplicationId)
.WithClientSecret(env.ClientSecret)
.WithTargetApplicationId(env.TargetApplicationId)
.Build();
.WithTargetApplicationId(env.TargetApplicationId);

if (testAuthMethod == "clientcert")
{
clientBuilder.WithClientSecret(env.ClientSecret);
}
else
{
var cert = X509Certificate2.CreateFromPemFile(env.ClientCertificatePath);
clientBuilder.WithClientCertificate(cert);
}

IAzureGraphClient client = clientBuilder.Build();

// Act
OperationResult<IEnumerable<string>> operationResult = client.DiscoverApplicationIds();
Expand Down
8 changes: 8 additions & 0 deletions AzureEnterpriseApplicationOrchestrator.Tests/FakeClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

using System.Security.Cryptography.X509Certificates;
using AzureEnterpriseApplicationOrchestrator.Client;
using Keyfactor.Logging;
using Keyfactor.Orchestrators.Common.Enums;
Expand All @@ -31,6 +32,7 @@ public class FakeBuilder : IAzureGraphClientBuilder
public string? _targetApplicationId { get; set; }
public string? _applicationId { get; set; }
public string? _clientSecret { get; set; }
public X509Certificate2? _clientCertificate { get; set; }
public string? _azureCloudEndpoint { get; set; }

public IAzureGraphClientBuilder WithTenantId(string tenantId)
Expand All @@ -57,6 +59,12 @@ public IAzureGraphClientBuilder WithClientSecret(string clientSecret)
return this;
}

public IAzureGraphClientBuilder WithClientCertificate(X509Certificate2 clientCertificate)
{
_clientCertificate = clientCertificate;
return this;
}

public IAzureGraphClientBuilder WithAzureCloud(string azureCloud)
{
_azureCloudEndpoint = azureCloud;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public sealed class IntegrationTestingFact : FactAttribute
public string TenantId { get; private set; }
public string ApplicationId { get; private set; }
public string ClientSecret { get; private set; }
public string ClientCertificatePath { get; private set; }

public string TargetApplicationId { get; private set; }

Expand All @@ -27,6 +28,7 @@ public IntegrationTestingFact()
TenantId = Environment.GetEnvironmentVariable("AZURE_TENANT_ID") ?? string.Empty;
ApplicationId = Environment.GetEnvironmentVariable("AZURE_CLIENT_ID") ?? string.Empty;
ClientSecret = Environment.GetEnvironmentVariable("AZURE_CLIENT_SECRET") ?? string.Empty;
ClientCertificatePath = Environment.GetEnvironmentVariable("AZURE_PATH_TO_CLIENT_CERTIFICATE") ?? string.Empty;

TargetApplicationId = Environment.GetEnvironmentVariable("AZURE_TARGET_APPLICATION_ID") ?? string.Empty;

Expand All @@ -37,3 +39,27 @@ public IntegrationTestingFact()
}
}

public sealed class IntegrationTestingTheory : TheoryAttribute
{
public string TenantId { get; private set; }
public string ApplicationId { get; private set; }
public string ClientSecret { get; private set; }
public string ClientCertificatePath { get; private set; }

public string TargetApplicationId { get; private set; }

public IntegrationTestingTheory()
{
TenantId = Environment.GetEnvironmentVariable("AZURE_TENANT_ID") ?? string.Empty;
ApplicationId = Environment.GetEnvironmentVariable("AZURE_CLIENT_ID") ?? string.Empty;
ClientSecret = Environment.GetEnvironmentVariable("AZURE_CLIENT_SECRET") ?? string.Empty;
ClientCertificatePath = Environment.GetEnvironmentVariable("AZURE_PATH_TO_CLIENT_CERTIFICATE") ?? string.Empty;

TargetApplicationId = Environment.GetEnvironmentVariable("AZURE_TARGET_APPLICATION_ID") ?? string.Empty;

if (string.IsNullOrEmpty(TenantId) || string.IsNullOrEmpty(ApplicationId) || string.IsNullOrEmpty(ClientSecret) || string.IsNullOrEmpty(TargetApplicationId))
{
Skip = "Integration testing environment variables are not set - Skipping test. Please run `make setup` to set the environment variables.";
}
}
}
92 changes: 91 additions & 1 deletion AzureEnterpriseApplicationOrchestrator.Tests/JobClientBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.

using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using AzureEnterpriseApplicationOrchestrator;
using AzureEnterpriseApplicationOrchestrator.Client;
using AzureEnterpriseApplicationOrchestrator.Tests;
Expand All @@ -32,7 +35,7 @@ public AzureEnterpriseApplicationOrchestrator_JobClientBuilder()
}

[Fact]
public void GraphJobClientBuilder_ValidCertificateStoreConfig_BuildValidClient()
public void GraphJobClientBuilder_ValidCertificateStoreConfigWithClientSecret_BuildValidClient()
{
// Verify that the GraphJobClientBuilder uses the certificate store configuration
// provided by Keyfactor Command/the Universal Orchestrator correctly as required
Expand Down Expand Up @@ -68,6 +71,93 @@ public void GraphJobClientBuilder_ValidCertificateStoreConfig_BuildValidClient()

_logger.LogInformation("GraphJobClientBuilder_ValidCertificateStoreConfig_BuildValidClient - Success");
}

[IntegrationTestingTheory]
[InlineData("pkcs12")]
[InlineData("pem")]
[InlineData("encryptedPem")]
public void GraphJobClientBuilder_ValidCertificateStoreConfigWithClientCertificate_BuildValidClient(string certificateFormat)
{
// Verify that the GraphJobClientBuilder uses the certificate store configuration
// provided by Keyfactor Command/the Universal Orchestrator correctly as required
// by the IAzureGraphClientBuilder interface.

// Arrange
GraphJobClientBuilder<FakeClient.FakeBuilder> jobClientBuilderWithFakeBuilder = new();

string password = "passwordpasswordpassword";
string certName = "SPTest" + Guid.NewGuid().ToString()[..6];
X509Certificate2 ssCert = GetSelfSignedCert(certName);

string b64ClientCertificate;
if (certificateFormat == "pkcs12")
{
b64ClientCertificate = Convert.ToBase64String(ssCert.Export(X509ContentType.Pfx, password));
}
else if (certificateFormat == "pem")
{
string pemCert = ssCert.ExportCertificatePem();
string keyPem = ssCert.GetRSAPrivateKey()!.ExportPkcs8PrivateKeyPem();
b64ClientCertificate = Convert.ToBase64String(Encoding.UTF8.GetBytes(keyPem + '\n' + pemCert));
password = "";
}
else
{
PbeParameters pbeParameters = new PbeParameters(
PbeEncryptionAlgorithm.Aes256Cbc,
HashAlgorithmName.SHA384,
300_000);
string pemCert = ssCert.ExportCertificatePem();
string keyPem = ssCert.GetRSAPrivateKey()!.ExportEncryptedPkcs8PrivateKeyPem(password.ToCharArray(), pbeParameters);
b64ClientCertificate = Convert.ToBase64String(Encoding.UTF8.GetBytes(keyPem + '\n' + pemCert));
}

// Set up the certificate store with names that correspond to how we expect them to be interpreted by
// the builder
CertificateStore fakeCertificateStoreDetails = new()
{
ClientMachine = "fake-tenant-id",
StorePath = "fake-azure-target-application-id",
Properties = $@"{{""ServerUsername"": ""fake-azure-application-id"",""ServerPassword"": ""{password}"",""ClientCertificate"": ""{b64ClientCertificate}"",""AzureCloud"": ""fake-azure-cloud""}}"
};

// Act
IAzureGraphClient fakeAppGatewayClient = jobClientBuilderWithFakeBuilder
.WithCertificateStoreDetails(fakeCertificateStoreDetails)
.Build();

// Assert

// IAzureGraphClient doesn't require any of the properties set by the builder to be exposed
// since the production Build() method creates an Azure Resource Manager client.
// But, our builder is fake and exposes the properties we need to test (via the FakeBuilder class).
Assert.Equal("fake-tenant-id", jobClientBuilderWithFakeBuilder._builder._tenantId);
Assert.Equal("fake-azure-target-application-id", jobClientBuilderWithFakeBuilder._builder._targetApplicationId);
Assert.Equal("fake-azure-application-id", jobClientBuilderWithFakeBuilder._builder._applicationId);
Assert.Equal("fake-azure-cloud", jobClientBuilderWithFakeBuilder._builder._azureCloudEndpoint);
Assert.Equal(ssCert.GetCertHash(), jobClientBuilderWithFakeBuilder._builder._clientCertificate!.GetCertHash());
Assert.NotNull(jobClientBuilderWithFakeBuilder._builder._clientCertificate!.GetRSAPrivateKey());
Assert.Equal(jobClientBuilderWithFakeBuilder._builder._clientCertificate!.GetRSAPrivateKey()!.ExportRSAPrivateKeyPem(), ssCert.GetRSAPrivateKey()!.ExportRSAPrivateKeyPem());

_logger.LogInformation("GraphJobClientBuilder_ValidCertificateStoreConfig_BuildValidClient - Success");
}

public static X509Certificate2 GetSelfSignedCert(string hostname)
{
RSA rsa = RSA.Create(2048);
CertificateRequest req = new CertificateRequest($"CN={hostname}", rsa, HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1);

SubjectAlternativeNameBuilder subjectAlternativeNameBuilder = new SubjectAlternativeNameBuilder();
subjectAlternativeNameBuilder.AddDnsName(hostname);
req.CertificateExtensions.Add(subjectAlternativeNameBuilder.Build());
req.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false));
req.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid("2.5.29.32.0"), new Oid("1.3.6.1.5.5.7.3.1") }, false));

X509Certificate2 selfSignedCert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(5));
Console.Write($"Created self-signed certificate for \"{hostname}\" with thumbprint {selfSignedCert.Thumbprint}\n");
return selfSignedCert;
}

static void ConfigureLogging()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
Expand All @@ -10,16 +10,16 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Azure.Core" Version="1.38.0" />
<PackageReference Include="Azure.Identity" Version="1.10.4" />
<PackageReference Include="Azure.ResourceManager" Version="1.10.2" />
<PackageReference Include="coverlet.collector" Version="6.0.1">
<PackageReference Include="Azure.Core" Version="1.39.0" />
<PackageReference Include="Azure.Identity" Version="1.11.3" />
<PackageReference Include="Azure.ResourceManager" Version="1.12.0" />
<PackageReference Include="coverlet.collector" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Keyfactor.Logging" Version="1.1.1" />
<PackageReference Include="Keyfactor.Orchestrators.IOrchestratorJobExtensions" Version="0.7.0" />
<PackageReference Include="Microsoft.Graph" Version="5.44.0" />
<PackageReference Include="Microsoft.Graph" Version="5.54.0" />
</ItemGroup>

<ItemGroup>
Expand Down
28 changes: 25 additions & 3 deletions AzureEnterpriseApplicationOrchestrator/Client/GraphClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public class Builder : IAzureGraphClientBuilder
private string _tenantId { get; set; }
private string _applicationId { get; set; }
private string _clientSecret { get; set; }
private X509Certificate2 _clientCertificate { get; set; }
private string _targetApplicationId { get; set; }
private Uri _azureCloudEndpoint { get; set; }

Expand Down Expand Up @@ -91,6 +92,12 @@ public IAzureGraphClientBuilder WithClientSecret(string clientSecret)
return this;
}

public IAzureGraphClientBuilder WithClientCertificate(X509Certificate2 clientCertificate)
{
_clientCertificate = clientCertificate;
return this;
}

public IAzureGraphClientBuilder WithAzureCloud(string azureCloud)
{
if (string.IsNullOrWhiteSpace(azureCloud))
Expand Down Expand Up @@ -129,9 +136,24 @@ public IAzureGraphClient Build()
AdditionallyAllowedTenants = { "*" }
};

TokenCredential credential = new ClientSecretCredential(
_tenantId, _applicationId, _clientSecret, credentialOptions
);
TokenCredential credential;
if (!string.IsNullOrWhiteSpace(_clientSecret))
{
credential = new ClientSecretCredential(
_tenantId, _applicationId, _clientSecret, credentialOptions
);
}
else if (_clientCertificate != null)
{
credential = new ClientCertificateCredential(
_tenantId, _applicationId, _clientCertificate, credentialOptions
);
}
else
{
throw new Exception("Client secret or client certificate must be provided.");
}


string[] scopes = { "https://graph.microsoft.com/.default" };

Expand Down
Loading
Loading