feat(pki): add Certificate Application + Enrollment models and client methods

Adds InfisicalCertificateApplication and InfisicalCertificateApplicationEnrollment models (with SCEP/EST/ACME/API sub-blocks) and DTO/mapper layer. Mapper computes the SHA-1 RA certificate thumbprint from the enrollment PEM so it can be fed directly into MDM payloads. InfisicalPkiClient gains ListCertificateApplications, GetCertificateApplication, GetCertificateApplicationByName, ListCertificateApplicationProfiles, GetCertificateApplicationEnrollment, and GenerateScepDynamicChallenge. InfisicalApiInvoker accepts an optional extraHeaders argument so callers can attach x-infisical-project-id and override Accept (used by the plain-text SCEP challenge endpoint). New endpoint names and registry entries cover /api/v1/cert-manager/applications/** and /scep/applications/**/profiles/**/challenge.
This commit is contained in:
GraceSolutions
2026-06-04 19:34:43 -04:00
parent 485ee8a7dd
commit a195901a10
8 changed files with 666 additions and 5 deletions
@@ -63,5 +63,13 @@ namespace PSInfisicalAPI.Endpoints
public const string GetCertificatePolicy = "GetCertificatePolicy"; public const string GetCertificatePolicy = "GetCertificatePolicy";
public const string ListCertificateAuthorities = "ListCertificateAuthorities"; public const string ListCertificateAuthorities = "ListCertificateAuthorities";
public const string ListCertificateApplications = "ListCertificateApplications";
public const string GetCertificateApplication = "GetCertificateApplication";
public const string GetCertificateApplicationByName = "GetCertificateApplicationByName";
public const string ListCertificateApplicationProfiles = "ListCertificateApplicationProfiles";
public const string GetCertificateApplicationEnrollment = "GetCertificateApplicationEnrollment";
public const string GenerateScepDynamicChallenge = "GenerateScepDynamicChallenge";
} }
} }
@@ -703,6 +703,67 @@ namespace PSInfisicalAPI.Endpoints
Template = "/api/v1/cert-manager/ca", Template = "/api/v1/cert-manager/ca",
RequiresAuthorization = true RequiresAuthorization = true
}); });
Add(map, new InfisicalEndpointDefinition
{
Name = InfisicalEndpointNames.ListCertificateApplications,
Resource = "Pki",
Version = "v1",
Method = "GET",
Template = "/api/v1/cert-manager/applications",
RequiresAuthorization = true
});
Add(map, new InfisicalEndpointDefinition
{
Name = InfisicalEndpointNames.GetCertificateApplication,
Resource = "Pki",
Version = "v1",
Method = "GET",
Template = "/api/v1/cert-manager/applications/{applicationId}",
RequiresAuthorization = true
});
Add(map, new InfisicalEndpointDefinition
{
Name = InfisicalEndpointNames.GetCertificateApplicationByName,
Resource = "Pki",
Version = "v1",
Method = "GET",
Template = "/api/v1/cert-manager/applications/by-name/{name}",
RequiresAuthorization = true
});
Add(map, new InfisicalEndpointDefinition
{
Name = InfisicalEndpointNames.ListCertificateApplicationProfiles,
Resource = "Pki",
Version = "v1",
Method = "GET",
Template = "/api/v1/cert-manager/applications/{applicationId}/profiles",
RequiresAuthorization = true
});
Add(map, new InfisicalEndpointDefinition
{
Name = InfisicalEndpointNames.GetCertificateApplicationEnrollment,
Resource = "Pki",
Version = "v1",
Method = "GET",
Template = "/api/v1/cert-manager/applications/{applicationId}/profiles/{profileId}/enrollment",
RequiresAuthorization = true
});
Add(map, new InfisicalEndpointDefinition
{
Name = InfisicalEndpointNames.GenerateScepDynamicChallenge,
Resource = "Pki",
Version = "v1",
Method = "POST",
Template = "/scep/applications/{applicationId}/profiles/{profileId}/challenge",
RequiresAuthorization = true,
ContainsSecretMaterialInResponse = true
});
} }
public static InfisicalEndpointDefinition Get(string name) public static InfisicalEndpointDefinition Get(string name)
+17 -5
View File
@@ -23,7 +23,8 @@ namespace PSInfisicalAPI.Http
string operationName, string operationName,
IDictionary<string, string> pathParameters, IDictionary<string, string> pathParameters,
IEnumerable<KeyValuePair<string, string>> queryParameters, IEnumerable<KeyValuePair<string, string>> queryParameters,
string body) string body,
IDictionary<string, string> extraHeaders = null)
{ {
if (connection == null) { throw new ArgumentNullException(nameof(connection)); } if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
if (string.IsNullOrEmpty(endpointName)) { throw new ArgumentNullException(nameof(endpointName)); } if (string.IsNullOrEmpty(endpointName)) { throw new ArgumentNullException(nameof(endpointName)); }
@@ -31,7 +32,7 @@ namespace PSInfisicalAPI.Http
InfisicalEndpointDefinition definition = InfisicalEndpointRegistry.Get(endpointName); InfisicalEndpointDefinition definition = InfisicalEndpointRegistry.Get(endpointName);
Uri uri = InfisicalUriBuilder.Build(connection.BaseUri, definition, pathParameters, queryParameters); Uri uri = InfisicalUriBuilder.Build(connection.BaseUri, definition, pathParameters, queryParameters);
InfisicalHttpResponse response = ExecuteAuthorized(connection, definition, operationName, uri, body); InfisicalHttpResponse response = ExecuteAuthorized(connection, definition, operationName, uri, body, extraHeaders);
if (response.StatusCode >= 200 && response.StatusCode < 300) if (response.StatusCode >= 200 && response.StatusCode < 300)
{ {
@@ -49,7 +50,8 @@ namespace PSInfisicalAPI.Http
string operationName, string operationName,
IDictionary<string, string> pathParameters, IDictionary<string, string> pathParameters,
IEnumerable<KeyValuePair<string, string>> queryParameters, IEnumerable<KeyValuePair<string, string>> queryParameters,
string body) string body,
IDictionary<string, string> extraHeaders = null)
{ {
if (connection == null) { throw new ArgumentNullException(nameof(connection)); } if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
if (string.IsNullOrEmpty(endpointName)) { throw new ArgumentNullException(nameof(endpointName)); } if (string.IsNullOrEmpty(endpointName)) { throw new ArgumentNullException(nameof(endpointName)); }
@@ -61,7 +63,7 @@ namespace PSInfisicalAPI.Http
{ {
InfisicalEndpointDefinition definition = candidates[index]; InfisicalEndpointDefinition definition = candidates[index];
Uri uri = InfisicalUriBuilder.Build(connection.BaseUri, definition, pathParameters, queryParameters); Uri uri = InfisicalUriBuilder.Build(connection.BaseUri, definition, pathParameters, queryParameters);
InfisicalHttpResponse response = ExecuteAuthorized(connection, definition, operationName, uri, body); InfisicalHttpResponse response = ExecuteAuthorized(connection, definition, operationName, uri, body, extraHeaders);
if (response.StatusCode >= 200 && response.StatusCode < 300) if (response.StatusCode >= 200 && response.StatusCode < 300)
{ {
@@ -95,7 +97,8 @@ namespace PSInfisicalAPI.Http
InfisicalEndpointDefinition definition, InfisicalEndpointDefinition definition,
string operationName, string operationName,
Uri uri, Uri uri,
string body) string body,
IDictionary<string, string> extraHeaders = null)
{ {
Dictionary<string, string> headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); Dictionary<string, string> headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
headers["Accept"] = "application/json"; headers["Accept"] = "application/json";
@@ -118,6 +121,15 @@ namespace PSInfisicalAPI.Http
}); });
} }
if (extraHeaders != null)
{
foreach (KeyValuePair<string, string> entry in extraHeaders)
{
if (string.IsNullOrEmpty(entry.Key)) { continue; }
headers[entry.Key] = entry.Value;
}
}
InfisicalHttpRequest request = new InfisicalHttpRequest InfisicalHttpRequest request = new InfisicalHttpRequest
{ {
OperationName = operationName, OperationName = operationName,
@@ -0,0 +1,31 @@
using System;
namespace PSInfisicalAPI.Models
{
public sealed class InfisicalCertificateApplication
{
public string Id { get; set; }
public string ProjectId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int? ProfileCount { get; set; }
public int? MemberCount { get; set; }
public int? CertificateCount { get; set; }
public DateTimeOffset? CreatedAtUtc { get; set; }
public DateTimeOffset? UpdatedAtUtc { get; set; }
}
public sealed class InfisicalCertificateApplicationProfileAttachment
{
public string ApplicationId { get; set; }
public string ProfileId { get; set; }
public string ProfileSlug { get; set; }
public string ProfileDescription { get; set; }
public string ApiConfigId { get; set; }
public string EstConfigId { get; set; }
public string AcmeConfigId { get; set; }
public string ScepConfigId { get; set; }
public DateTimeOffset? CreatedAtUtc { get; set; }
public DateTimeOffset? UpdatedAtUtc { get; set; }
}
}
@@ -0,0 +1,55 @@
using System;
namespace PSInfisicalAPI.Models
{
public sealed class InfisicalCertificateApplicationEnrollment
{
public string ApplicationId { get; set; }
public string ProfileId { get; set; }
public InfisicalCertificateApplicationApiEnrollment Api { get; set; }
public InfisicalCertificateApplicationEstEnrollment Est { get; set; }
public InfisicalCertificateApplicationAcmeEnrollment Acme { get; set; }
public InfisicalCertificateApplicationScepEnrollment Scep { get; set; }
public bool ApiConfigured { get { return Api != null; } }
public bool EstConfigured { get; set; }
public bool AcmeConfigured { get; set; }
public bool ScepConfigured { get; set; }
}
public sealed class InfisicalCertificateApplicationApiEnrollment
{
public string Id { get; set; }
public bool? AutoRenew { get; set; }
public int? RenewBeforeDays { get; set; }
}
public sealed class InfisicalCertificateApplicationEstEnrollment
{
public string Id { get; set; }
public bool? DisableBootstrapCaValidation { get; set; }
public string EstEndpointUrl { get; set; }
}
public sealed class InfisicalCertificateApplicationAcmeEnrollment
{
public string Id { get; set; }
public bool? SkipDnsOwnershipVerification { get; set; }
public bool? SkipEabBinding { get; set; }
public string DirectoryUrl { get; set; }
}
public sealed class InfisicalCertificateApplicationScepEnrollment
{
public string Id { get; set; }
public string ChallengeType { get; set; }
public bool? IncludeCaCertInResponse { get; set; }
public bool? AllowCertBasedRenewal { get; set; }
public int? DynamicChallengeExpiryMinutes { get; set; }
public int? DynamicChallengeMaxPending { get; set; }
public string ScepEndpointUrl { get; set; }
public string ChallengeEndpointUrl { get; set; }
public string RaCertificatePem { get; set; }
public string RaCertificateThumbprint { get; set; }
public DateTimeOffset? RaCertExpiresAtUtc { get; set; }
}
}
@@ -0,0 +1,92 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace PSInfisicalAPI.Pki
{
internal sealed class InfisicalCertificateApplicationResponseDto
{
[JsonProperty("id")] public string Id { get; set; }
[JsonProperty("projectId")] public string ProjectId { get; set; }
[JsonProperty("name")] public string Name { get; set; }
[JsonProperty("description")] public string Description { get; set; }
[JsonProperty("profileCount")] public int? ProfileCount { get; set; }
[JsonProperty("memberCount")] public int? MemberCount { get; set; }
[JsonProperty("certificateCount")] public int? CertificateCount { get; set; }
[JsonProperty("createdAt")] public string CreatedAt { get; set; }
[JsonProperty("updatedAt")] public string UpdatedAt { get; set; }
}
internal sealed class InfisicalCertificateApplicationListResponseDto
{
[JsonProperty("applications")] public List<InfisicalCertificateApplicationResponseDto> Applications { get; set; }
[JsonProperty("total")] public int? Total { get; set; }
}
internal sealed class InfisicalCertificateApplicationProfileAttachmentDto
{
[JsonProperty("applicationId")] public string ApplicationId { get; set; }
[JsonProperty("profileId")] public string ProfileId { get; set; }
[JsonProperty("profileSlug")] public string ProfileSlug { get; set; }
[JsonProperty("profileDescription")] public string ProfileDescription { get; set; }
[JsonProperty("apiConfigId")] public string ApiConfigId { get; set; }
[JsonProperty("estConfigId")] public string EstConfigId { get; set; }
[JsonProperty("acmeConfigId")] public string AcmeConfigId { get; set; }
[JsonProperty("scepConfigId")] public string ScepConfigId { get; set; }
[JsonProperty("createdAt")] public string CreatedAt { get; set; }
[JsonProperty("updatedAt")] public string UpdatedAt { get; set; }
}
internal sealed class InfisicalCertificateApplicationProfilesResponseDto
{
[JsonProperty("profiles")] public List<InfisicalCertificateApplicationProfileAttachmentDto> Profiles { get; set; }
}
internal sealed class InfisicalCertificateApplicationEnrollmentResponseDto
{
[JsonProperty("applicationId")] public string ApplicationId { get; set; }
[JsonProperty("profileId")] public string ProfileId { get; set; }
[JsonProperty("api")] public InfisicalCertificateApplicationApiEnrollmentDto Api { get; set; }
[JsonProperty("est")] public InfisicalCertificateApplicationEstEnrollmentDto Est { get; set; }
[JsonProperty("acme")] public InfisicalCertificateApplicationAcmeEnrollmentDto Acme { get; set; }
[JsonProperty("scep")] public InfisicalCertificateApplicationScepEnrollmentDto Scep { get; set; }
[JsonProperty("estConfigured")] public bool? EstConfigured { get; set; }
[JsonProperty("acmeConfigured")] public bool? AcmeConfigured { get; set; }
[JsonProperty("scepConfigured")] public bool? ScepConfigured { get; set; }
}
internal sealed class InfisicalCertificateApplicationApiEnrollmentDto
{
[JsonProperty("id")] public string Id { get; set; }
[JsonProperty("autoRenew")] public bool? AutoRenew { get; set; }
[JsonProperty("renewBeforeDays")] public int? RenewBeforeDays { get; set; }
}
internal sealed class InfisicalCertificateApplicationEstEnrollmentDto
{
[JsonProperty("id")] public string Id { get; set; }
[JsonProperty("disableBootstrapCaValidation")] public bool? DisableBootstrapCaValidation { get; set; }
[JsonProperty("estEndpointUrl")] public string EstEndpointUrl { get; set; }
}
internal sealed class InfisicalCertificateApplicationAcmeEnrollmentDto
{
[JsonProperty("id")] public string Id { get; set; }
[JsonProperty("skipDnsOwnershipVerification")] public bool? SkipDnsOwnershipVerification { get; set; }
[JsonProperty("skipEabBinding")] public bool? SkipEabBinding { get; set; }
[JsonProperty("directoryUrl")] public string DirectoryUrl { get; set; }
}
internal sealed class InfisicalCertificateApplicationScepEnrollmentDto
{
[JsonProperty("id")] public string Id { get; set; }
[JsonProperty("challengeType")] public string ChallengeType { get; set; }
[JsonProperty("includeCaCertInResponse")] public bool? IncludeCaCertInResponse { get; set; }
[JsonProperty("allowCertBasedRenewal")] public bool? AllowCertBasedRenewal { get; set; }
[JsonProperty("dynamicChallengeExpiryMinutes")] public int? DynamicChallengeExpiryMinutes { get; set; }
[JsonProperty("dynamicChallengeMaxPending")] public int? DynamicChallengeMaxPending { get; set; }
[JsonProperty("scepEndpointUrl")] public string ScepEndpointUrl { get; set; }
[JsonProperty("challengeEndpointUrl")] public string ChallengeEndpointUrl { get; set; }
[JsonProperty("raCertificatePem")] public string RaCertificatePem { get; set; }
[JsonProperty("raCertExpiresAt")] public string RaCertExpiresAt { get; set; }
}
}
@@ -0,0 +1,175 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using PSInfisicalAPI.Models;
namespace PSInfisicalAPI.Pki
{
internal static class InfisicalCertificateApplicationMapper
{
public static InfisicalCertificateApplication Map(InfisicalCertificateApplicationResponseDto dto, string fallbackProjectId)
{
if (dto == null) { return null; }
return new InfisicalCertificateApplication
{
Id = dto.Id,
ProjectId = !string.IsNullOrEmpty(dto.ProjectId) ? dto.ProjectId : fallbackProjectId,
Name = dto.Name,
Description = dto.Description,
ProfileCount = dto.ProfileCount,
MemberCount = dto.MemberCount,
CertificateCount = dto.CertificateCount,
CreatedAtUtc = ParseTimestamp(dto.CreatedAt),
UpdatedAtUtc = ParseTimestamp(dto.UpdatedAt)
};
}
public static InfisicalCertificateApplication[] MapMany(IEnumerable<InfisicalCertificateApplicationResponseDto> items, string fallbackProjectId)
{
if (items == null) { return Array.Empty<InfisicalCertificateApplication>(); }
List<InfisicalCertificateApplication> results = new List<InfisicalCertificateApplication>();
foreach (InfisicalCertificateApplicationResponseDto dto in items)
{
InfisicalCertificateApplication mapped = Map(dto, fallbackProjectId);
if (mapped != null) { results.Add(mapped); }
}
return results.ToArray();
}
public static InfisicalCertificateApplicationProfileAttachment MapAttachment(InfisicalCertificateApplicationProfileAttachmentDto dto)
{
if (dto == null) { return null; }
return new InfisicalCertificateApplicationProfileAttachment
{
ApplicationId = dto.ApplicationId,
ProfileId = dto.ProfileId,
ProfileSlug = dto.ProfileSlug,
ProfileDescription = dto.ProfileDescription,
ApiConfigId = dto.ApiConfigId,
EstConfigId = dto.EstConfigId,
AcmeConfigId = dto.AcmeConfigId,
ScepConfigId = dto.ScepConfigId,
CreatedAtUtc = ParseTimestamp(dto.CreatedAt),
UpdatedAtUtc = ParseTimestamp(dto.UpdatedAt)
};
}
public static InfisicalCertificateApplicationProfileAttachment[] MapAttachments(IEnumerable<InfisicalCertificateApplicationProfileAttachmentDto> items)
{
if (items == null) { return Array.Empty<InfisicalCertificateApplicationProfileAttachment>(); }
List<InfisicalCertificateApplicationProfileAttachment> results = new List<InfisicalCertificateApplicationProfileAttachment>();
foreach (InfisicalCertificateApplicationProfileAttachmentDto dto in items)
{
InfisicalCertificateApplicationProfileAttachment mapped = MapAttachment(dto);
if (mapped != null) { results.Add(mapped); }
}
return results.ToArray();
}
public static InfisicalCertificateApplicationEnrollment MapEnrollment(InfisicalCertificateApplicationEnrollmentResponseDto dto)
{
if (dto == null) { return null; }
return new InfisicalCertificateApplicationEnrollment
{
ApplicationId = dto.ApplicationId,
ProfileId = dto.ProfileId,
Api = MapApi(dto.Api),
Est = MapEst(dto.Est),
Acme = MapAcme(dto.Acme),
Scep = MapScep(dto.Scep),
EstConfigured = dto.EstConfigured.GetValueOrDefault(),
AcmeConfigured = dto.AcmeConfigured.GetValueOrDefault(),
ScepConfigured = dto.ScepConfigured.GetValueOrDefault()
};
}
private static InfisicalCertificateApplicationApiEnrollment MapApi(InfisicalCertificateApplicationApiEnrollmentDto dto)
{
if (dto == null) { return null; }
return new InfisicalCertificateApplicationApiEnrollment { Id = dto.Id, AutoRenew = dto.AutoRenew, RenewBeforeDays = dto.RenewBeforeDays };
}
private static InfisicalCertificateApplicationEstEnrollment MapEst(InfisicalCertificateApplicationEstEnrollmentDto dto)
{
if (dto == null) { return null; }
return new InfisicalCertificateApplicationEstEnrollment { Id = dto.Id, DisableBootstrapCaValidation = dto.DisableBootstrapCaValidation, EstEndpointUrl = dto.EstEndpointUrl };
}
private static InfisicalCertificateApplicationAcmeEnrollment MapAcme(InfisicalCertificateApplicationAcmeEnrollmentDto dto)
{
if (dto == null) { return null; }
return new InfisicalCertificateApplicationAcmeEnrollment { Id = dto.Id, SkipDnsOwnershipVerification = dto.SkipDnsOwnershipVerification, SkipEabBinding = dto.SkipEabBinding, DirectoryUrl = dto.DirectoryUrl };
}
private static InfisicalCertificateApplicationScepEnrollment MapScep(InfisicalCertificateApplicationScepEnrollmentDto dto)
{
if (dto == null) { return null; }
return new InfisicalCertificateApplicationScepEnrollment
{
Id = dto.Id,
ChallengeType = dto.ChallengeType,
IncludeCaCertInResponse = dto.IncludeCaCertInResponse,
AllowCertBasedRenewal = dto.AllowCertBasedRenewal,
DynamicChallengeExpiryMinutes = dto.DynamicChallengeExpiryMinutes,
DynamicChallengeMaxPending = dto.DynamicChallengeMaxPending,
ScepEndpointUrl = dto.ScepEndpointUrl,
ChallengeEndpointUrl = dto.ChallengeEndpointUrl,
RaCertificatePem = dto.RaCertificatePem,
RaCertificateThumbprint = ComputeThumbprint(dto.RaCertificatePem),
RaCertExpiresAtUtc = ParseTimestamp(dto.RaCertExpiresAt)
};
}
internal static string ComputeThumbprint(string pem)
{
if (string.IsNullOrEmpty(pem)) { return null; }
try
{
byte[] der = Convert.FromBase64String(StripPemArmor(pem));
using (X509Certificate2 cert = new X509Certificate2(der))
{
return cert.Thumbprint;
}
}
catch
{
return null;
}
}
private static string StripPemArmor(string pem)
{
StringBuilder sb = new StringBuilder(pem.Length);
using (StringReader reader = new StringReader(pem))
{
string line;
while ((line = reader.ReadLine()) != null)
{
string trimmed = line.Trim();
if (trimmed.Length == 0) { continue; }
if (trimmed.StartsWith("-----", StringComparison.Ordinal)) { continue; }
sb.Append(trimmed);
}
}
return sb.ToString();
}
private static DateTimeOffset? ParseTimestamp(string value)
{
if (string.IsNullOrEmpty(value)) { return null; }
DateTimeOffset parsed;
if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out parsed))
{
return parsed;
}
return null;
}
}
}
@@ -640,6 +640,233 @@ namespace PSInfisicalAPI.Pki
} }
} }
public InfisicalCertificateApplication[] ListCertificateApplications(InfisicalConnection connection, string projectId, int? limit, int? offset)
{
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
string resolvedProjectId = FirstNonEmpty(projectId, connection.ProjectId);
if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); }
List<KeyValuePair<string, string>> query = new List<KeyValuePair<string, string>>();
if (limit.HasValue) { query.Add(new KeyValuePair<string, string>("limit", limit.Value.ToString(CultureInfo.InvariantCulture))); }
if (offset.HasValue) { query.Add(new KeyValuePair<string, string>("offset", offset.Value.ToString(CultureInfo.InvariantCulture))); }
Dictionary<string, string> headers = BuildProjectHeader(resolvedProjectId);
try
{
_logger.Information(Component, "Attempting to list Infisical certificate applications. Please Wait...");
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.ListCertificateApplications, "ListCertificateApplications", null, query, null, headers);
string body = response.Body;
response.Clear();
List<InfisicalCertificateApplicationResponseDto> source = ParseApplicationListBody(body);
InfisicalCertificateApplication[] mapped = InfisicalCertificateApplicationMapper.MapMany(source, resolvedProjectId);
_logger.Information(Component, "Infisical certificate application list retrieval was successful.");
return mapped;
}
catch (Exception)
{
_logger.Error(Component, "Infisical certificate application list retrieval failed.");
throw;
}
}
public InfisicalCertificateApplication GetCertificateApplication(InfisicalConnection connection, string applicationId, string projectId)
{
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
if (string.IsNullOrEmpty(applicationId)) { throw new InfisicalConfigurationException("ApplicationId is required."); }
Dictionary<string, string> pathParameters = new Dictionary<string, string> { { "applicationId", applicationId } };
string resolvedProjectId = FirstNonEmpty(projectId, connection.ProjectId);
Dictionary<string, string> headers = !string.IsNullOrEmpty(resolvedProjectId) ? BuildProjectHeader(resolvedProjectId) : null;
try
{
_logger.Information(Component, string.Concat("Attempting to retrieve Infisical certificate application '", applicationId, "'. Please Wait..."));
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.GetCertificateApplication, "GetCertificateApplication", pathParameters, null, null, headers);
string body = response.Body;
response.Clear();
InfisicalCertificateApplicationResponseDto inner = ParseApplicationSingleBody(body);
InfisicalCertificateApplication mapped = InfisicalCertificateApplicationMapper.Map(inner, resolvedProjectId);
_logger.Information(Component, "Infisical certificate application retrieval was successful.");
return mapped;
}
catch (Exception)
{
_logger.Error(Component, "Infisical certificate application retrieval failed.");
throw;
}
}
public InfisicalCertificateApplication GetCertificateApplicationByName(InfisicalConnection connection, string name, string projectId)
{
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
if (string.IsNullOrEmpty(name)) { throw new InfisicalConfigurationException("ApplicationName is required."); }
string resolvedProjectId = FirstNonEmpty(projectId, connection.ProjectId);
if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); }
Dictionary<string, string> pathParameters = new Dictionary<string, string> { { "name", name } };
Dictionary<string, string> headers = BuildProjectHeader(resolvedProjectId);
try
{
_logger.Information(Component, string.Concat("Attempting to retrieve Infisical certificate application '", name, "' by name. Please Wait..."));
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.GetCertificateApplicationByName, "GetCertificateApplicationByName", pathParameters, null, null, headers);
string body = response.Body;
response.Clear();
InfisicalCertificateApplicationResponseDto inner = ParseApplicationSingleBody(body);
InfisicalCertificateApplication mapped = InfisicalCertificateApplicationMapper.Map(inner, resolvedProjectId);
_logger.Information(Component, "Infisical certificate application (by name) retrieval was successful.");
return mapped;
}
catch (Exception)
{
_logger.Error(Component, "Infisical certificate application (by name) retrieval failed.");
throw;
}
}
public InfisicalCertificateApplicationProfileAttachment[] ListCertificateApplicationProfiles(InfisicalConnection connection, string applicationId, string projectId)
{
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
if (string.IsNullOrEmpty(applicationId)) { throw new InfisicalConfigurationException("ApplicationId is required."); }
Dictionary<string, string> pathParameters = new Dictionary<string, string> { { "applicationId", applicationId } };
string resolvedProjectId = FirstNonEmpty(projectId, connection.ProjectId);
Dictionary<string, string> headers = !string.IsNullOrEmpty(resolvedProjectId) ? BuildProjectHeader(resolvedProjectId) : null;
try
{
_logger.Information(Component, string.Concat("Attempting to list profile attachments for Infisical certificate application '", applicationId, "'. Please Wait..."));
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.ListCertificateApplicationProfiles, "ListCertificateApplicationProfiles", pathParameters, null, null, headers);
string body = response.Body;
response.Clear();
List<InfisicalCertificateApplicationProfileAttachmentDto> source = ParseApplicationProfilesBody(body);
InfisicalCertificateApplicationProfileAttachment[] mapped = InfisicalCertificateApplicationMapper.MapAttachments(source);
_logger.Information(Component, "Infisical certificate application profile attachment listing was successful.");
return mapped;
}
catch (Exception)
{
_logger.Error(Component, "Infisical certificate application profile attachment listing failed.");
throw;
}
}
public InfisicalCertificateApplicationEnrollment GetCertificateApplicationEnrollment(InfisicalConnection connection, string applicationId, string profileId, string projectId)
{
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
if (string.IsNullOrEmpty(applicationId)) { throw new InfisicalConfigurationException("ApplicationId is required."); }
if (string.IsNullOrEmpty(profileId)) { throw new InfisicalConfigurationException("ProfileId is required."); }
Dictionary<string, string> pathParameters = new Dictionary<string, string>
{
{ "applicationId", applicationId },
{ "profileId", profileId }
};
string resolvedProjectId = FirstNonEmpty(projectId, connection.ProjectId);
Dictionary<string, string> headers = !string.IsNullOrEmpty(resolvedProjectId) ? BuildProjectHeader(resolvedProjectId) : null;
try
{
_logger.Information(Component, string.Concat("Attempting to retrieve enrollment for application '", applicationId, "' / profile '", profileId, "'. Please Wait..."));
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.GetCertificateApplicationEnrollment, "GetCertificateApplicationEnrollment", pathParameters, null, null, headers);
InfisicalCertificateApplicationEnrollmentResponseDto dto = _serializer.Deserialize<InfisicalCertificateApplicationEnrollmentResponseDto>(response.Body);
response.Clear();
InfisicalCertificateApplicationEnrollment mapped = InfisicalCertificateApplicationMapper.MapEnrollment(dto);
_logger.Information(Component, "Infisical certificate application enrollment retrieval was successful.");
return mapped;
}
catch (Exception)
{
_logger.Error(Component, "Infisical certificate application enrollment retrieval failed.");
throw;
}
}
public string GenerateScepDynamicChallenge(InfisicalConnection connection, string applicationId, string profileId)
{
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
if (string.IsNullOrEmpty(applicationId)) { throw new InfisicalConfigurationException("ApplicationId is required."); }
if (string.IsNullOrEmpty(profileId)) { throw new InfisicalConfigurationException("ProfileId is required."); }
Dictionary<string, string> pathParameters = new Dictionary<string, string>
{
{ "applicationId", applicationId },
{ "profileId", profileId }
};
Dictionary<string, string> headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{ "Accept", "text/plain" }
};
try
{
_logger.Information(Component, string.Concat("Attempting to generate SCEP dynamic challenge for application '", applicationId, "' / profile '", profileId, "'. Please Wait..."));
InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.GenerateScepDynamicChallenge, "GenerateScepDynamicChallenge", pathParameters, null, string.Empty, headers);
string body = response.Body != null ? response.Body.Trim() : null;
response.Clear();
if (string.IsNullOrEmpty(body)) { throw new InfisicalApiException("SCEP dynamic challenge response was empty."); }
_logger.Information(Component, "Infisical SCEP dynamic challenge generation was successful.");
return body;
}
catch (Exception)
{
_logger.Error(Component, "Infisical SCEP dynamic challenge generation failed.");
throw;
}
}
private static Dictionary<string, string> BuildProjectHeader(string projectId)
{
return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{ "x-infisical-project-id", projectId }
};
}
private List<InfisicalCertificateApplicationResponseDto> ParseApplicationListBody(string body)
{
if (string.IsNullOrEmpty(body)) { return null; }
JToken token = JToken.Parse(body);
if (token.Type == JTokenType.Array)
{
return token.ToObject<List<InfisicalCertificateApplicationResponseDto>>();
}
InfisicalCertificateApplicationListResponseDto wrapper = token.ToObject<InfisicalCertificateApplicationListResponseDto>();
return wrapper != null ? wrapper.Applications : null;
}
private InfisicalCertificateApplicationResponseDto ParseApplicationSingleBody(string body)
{
if (string.IsNullOrEmpty(body)) { return null; }
JToken token = JToken.Parse(body);
if (token.Type != JTokenType.Object) { return null; }
JObject obj = (JObject)token;
if (obj["application"] is JObject inner) { return inner.ToObject<InfisicalCertificateApplicationResponseDto>(); }
return obj.ToObject<InfisicalCertificateApplicationResponseDto>();
}
private List<InfisicalCertificateApplicationProfileAttachmentDto> ParseApplicationProfilesBody(string body)
{
if (string.IsNullOrEmpty(body)) { return null; }
JToken token = JToken.Parse(body);
if (token.Type == JTokenType.Array)
{
return token.ToObject<List<InfisicalCertificateApplicationProfileAttachmentDto>>();
}
InfisicalCertificateApplicationProfilesResponseDto wrapper = token.ToObject<InfisicalCertificateApplicationProfilesResponseDto>();
return wrapper != null ? wrapper.Profiles : null;
}
internal static InfisicalCertificateSearchRequestDto BuildSearchRequest(InfisicalCertificateSearchQuery query) internal static InfisicalCertificateSearchRequestDto BuildSearchRequest(InfisicalCertificateSearchQuery query)
{ {
return new InfisicalCertificateSearchRequestDto return new InfisicalCertificateSearchRequestDto