From 269f0ea438c66497a689dd767fabb42f6fdf5d72 Mon Sep 17 00:00:00 2001 From: GraceSolutions Date: Wed, 3 Jun 2026 17:02:10 -0400 Subject: [PATCH 01/12] Tests: roll forward to latest major .NET runtime Adds LatestMajor to the test project so the net8.0 testhost can run on hosts that only have a newer .NET runtime installed (e.g. CI hosts with .NET 10 only). Locally with .NET 8 present this is a no-op; on the runner with .NET 10.0.8 the testhost will roll forward instead of aborting with 'You must install or update .NET to run this application'. --- src/PSInfisicalAPI.Tests/PSInfisicalAPI.Tests.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PSInfisicalAPI.Tests/PSInfisicalAPI.Tests.csproj b/src/PSInfisicalAPI.Tests/PSInfisicalAPI.Tests.csproj index 80f740d..c602801 100644 --- a/src/PSInfisicalAPI.Tests/PSInfisicalAPI.Tests.csproj +++ b/src/PSInfisicalAPI.Tests/PSInfisicalAPI.Tests.csproj @@ -2,6 +2,7 @@ net8.0 + LatestMajor 9.0 false PSInfisicalAPI.Tests -- 2.52.0 From 612ecf2c7d79908e25bb8836060df067c5db5d38 Mon Sep 17 00:00:00 2001 From: GraceSolutions Date: Wed, 3 Jun 2026 17:17:53 -0400 Subject: [PATCH 02/12] M1: endpoint registry + shared API invoker for full CRUD expansion --- .../EndpointRegistryTests.cs | 34 ++ .../Endpoints/InfisicalEndpointNames.cs | 36 ++ .../Endpoints/InfisicalEndpointRegistry.cs | 504 +++++++++++++++--- .../Http/InfisicalApiInvoker.cs | 103 ++++ 4 files changed, 601 insertions(+), 76 deletions(-) create mode 100644 src/PSInfisicalAPI/Http/InfisicalApiInvoker.cs diff --git a/src/PSInfisicalAPI.Tests/EndpointRegistryTests.cs b/src/PSInfisicalAPI.Tests/EndpointRegistryTests.cs index ccd6d61..1edada8 100644 --- a/src/PSInfisicalAPI.Tests/EndpointRegistryTests.cs +++ b/src/PSInfisicalAPI.Tests/EndpointRegistryTests.cs @@ -45,5 +45,39 @@ namespace PSInfisicalAPI.Tests { Assert.Throws(() => InfisicalEndpointRegistry.Get("NotARealEndpoint")); } + + [Theory] + [InlineData(InfisicalEndpointNames.CreateSecret, "POST", "/api/v3/secrets/raw/{secretName}")] + [InlineData(InfisicalEndpointNames.UpdateSecret, "PATCH", "/api/v3/secrets/raw/{secretName}")] + [InlineData(InfisicalEndpointNames.DeleteSecret, "DELETE", "/api/v3/secrets/raw/{secretName}")] + [InlineData(InfisicalEndpointNames.ListProjects, "GET", "/api/v1/workspace")] + [InlineData(InfisicalEndpointNames.RetrieveProject, "GET", "/api/v1/workspace/{projectId}")] + [InlineData(InfisicalEndpointNames.CreateProject, "POST", "/api/v2/workspace")] + [InlineData(InfisicalEndpointNames.UpdateProject, "PATCH", "/api/v1/workspace/{projectId}")] + [InlineData(InfisicalEndpointNames.DeleteProject, "DELETE", "/api/v1/workspace/{projectId}")] + [InlineData(InfisicalEndpointNames.CreateEnvironment, "POST", "/api/v1/workspace/{projectId}/environments")] + [InlineData(InfisicalEndpointNames.UpdateEnvironment, "PATCH", "/api/v1/workspace/{projectId}/environments/{environmentId}")] + [InlineData(InfisicalEndpointNames.DeleteEnvironment, "DELETE", "/api/v1/workspace/{projectId}/environments/{environmentId}")] + [InlineData(InfisicalEndpointNames.ListFolders, "GET", "/api/v1/folders")] + [InlineData(InfisicalEndpointNames.CreateFolder, "POST", "/api/v1/folders")] + [InlineData(InfisicalEndpointNames.UpdateFolder, "PATCH", "/api/v1/folders/{folderId}")] + [InlineData(InfisicalEndpointNames.DeleteFolder, "DELETE", "/api/v1/folders/{folderId}")] + [InlineData(InfisicalEndpointNames.ListTags, "GET", "/api/v1/workspace/{projectId}/tags")] + [InlineData(InfisicalEndpointNames.CreateTag, "POST", "/api/v1/workspace/{projectId}/tags")] + [InlineData(InfisicalEndpointNames.UpdateTag, "PATCH", "/api/v1/workspace/{projectId}/tags/{tagId}")] + [InlineData(InfisicalEndpointNames.DeleteTag, "DELETE", "/api/v1/workspace/{projectId}/tags/{tagId}")] + [InlineData(InfisicalEndpointNames.JwtAuthLogin, "POST", "/api/v1/auth/jwt-auth/login")] + [InlineData(InfisicalEndpointNames.OidcAuthLogin, "POST", "/api/v1/auth/oidc-auth/login")] + [InlineData(InfisicalEndpointNames.LdapAuthLogin, "POST", "/api/v1/auth/ldap-auth/login")] + [InlineData(InfisicalEndpointNames.KubernetesAuthLogin, "POST", "/api/v1/auth/kubernetes-auth/login")] + [InlineData(InfisicalEndpointNames.AwsAuthLogin, "POST", "/api/v1/auth/aws-auth/login")] + [InlineData(InfisicalEndpointNames.AzureAuthLogin, "POST", "/api/v1/auth/azure-auth/login")] + [InlineData(InfisicalEndpointNames.GcpIamAuthLogin, "POST", "/api/v1/auth/gcp-auth/login")] + public void Registered_Endpoints_Have_Expected_Shape(string name, string method, string template) + { + InfisicalEndpointDefinition definition = InfisicalEndpointRegistry.Get(name); + Assert.Equal(method, definition.Method); + Assert.Equal(template, definition.Template); + } } } diff --git a/src/PSInfisicalAPI/Endpoints/InfisicalEndpointNames.cs b/src/PSInfisicalAPI/Endpoints/InfisicalEndpointNames.cs index 1bea19d..2d331ba 100644 --- a/src/PSInfisicalAPI/Endpoints/InfisicalEndpointNames.cs +++ b/src/PSInfisicalAPI/Endpoints/InfisicalEndpointNames.cs @@ -3,7 +3,43 @@ namespace PSInfisicalAPI.Endpoints public static class InfisicalEndpointNames { public const string UniversalAuthLogin = "UniversalAuthLogin"; + public const string TokenAuthLogin = "TokenAuthLogin"; + public const string JwtAuthLogin = "JwtAuthLogin"; + public const string OidcAuthLogin = "OidcAuthLogin"; + public const string LdapAuthLogin = "LdapAuthLogin"; + public const string KubernetesAuthLogin = "KubernetesAuthLogin"; + public const string AwsAuthLogin = "AwsAuthLogin"; + public const string AzureAuthLogin = "AzureAuthLogin"; + public const string GcpIamAuthLogin = "GcpIamAuthLogin"; + public const string ListSecrets = "ListSecrets"; public const string RetrieveSecret = "RetrieveSecret"; + public const string CreateSecret = "CreateSecret"; + public const string UpdateSecret = "UpdateSecret"; + public const string DeleteSecret = "DeleteSecret"; + + public const string ListProjects = "ListProjects"; + public const string RetrieveProject = "RetrieveProject"; + public const string CreateProject = "CreateProject"; + public const string UpdateProject = "UpdateProject"; + public const string DeleteProject = "DeleteProject"; + + public const string ListEnvironments = "ListEnvironments"; + public const string RetrieveEnvironment = "RetrieveEnvironment"; + public const string CreateEnvironment = "CreateEnvironment"; + public const string UpdateEnvironment = "UpdateEnvironment"; + public const string DeleteEnvironment = "DeleteEnvironment"; + + public const string ListFolders = "ListFolders"; + public const string RetrieveFolder = "RetrieveFolder"; + public const string CreateFolder = "CreateFolder"; + public const string UpdateFolder = "UpdateFolder"; + public const string DeleteFolder = "DeleteFolder"; + + public const string ListTags = "ListTags"; + public const string RetrieveTag = "RetrieveTag"; + public const string CreateTag = "CreateTag"; + public const string UpdateTag = "UpdateTag"; + public const string DeleteTag = "DeleteTag"; } } diff --git a/src/PSInfisicalAPI/Endpoints/InfisicalEndpointRegistry.cs b/src/PSInfisicalAPI/Endpoints/InfisicalEndpointRegistry.cs index 997d14f..1ccf72a 100644 --- a/src/PSInfisicalAPI/Endpoints/InfisicalEndpointRegistry.cs +++ b/src/PSInfisicalAPI/Endpoints/InfisicalEndpointRegistry.cs @@ -5,83 +5,435 @@ namespace PSInfisicalAPI.Endpoints { public static class InfisicalEndpointRegistry { - private static readonly Dictionary> Candidates = - new Dictionary> + private static readonly Dictionary> Candidates; + + static InfisicalEndpointRegistry() + { + Candidates = new Dictionary>(); + RegisterAuthentication(Candidates); + RegisterSecrets(Candidates); + RegisterProjects(Candidates); + RegisterEnvironments(Candidates); + RegisterFolders(Candidates); + RegisterTags(Candidates); + } + + private static void Add(Dictionary> map, InfisicalEndpointDefinition definition) + { + List list; + if (!map.TryGetValue(definition.Name, out list)) { - { - InfisicalEndpointNames.UniversalAuthLogin, - new List - { - new InfisicalEndpointDefinition - { - Name = InfisicalEndpointNames.UniversalAuthLogin, - Resource = "Authentication", - Version = "v1", - Method = "POST", - Template = "/api/v1/auth/universal-auth/login", - RequiresAuthorization = false, - ContainsSecretMaterialInRequest = true, - ContainsSecretMaterialInResponse = true - } - } - }, - { - InfisicalEndpointNames.ListSecrets, - new List - { - new InfisicalEndpointDefinition - { - Name = InfisicalEndpointNames.ListSecrets, - Resource = "Secrets", - Version = "v4", - Method = "GET", - Template = "/api/v4/secrets", - RequiresAuthorization = true, - ContainsSecretMaterialInRequest = false, - ContainsSecretMaterialInResponse = true - }, - new InfisicalEndpointDefinition - { - Name = InfisicalEndpointNames.ListSecrets, - Resource = "Secrets", - Version = "v3", - Method = "GET", - Template = "/api/v3/secrets/raw", - RequiresAuthorization = true, - ContainsSecretMaterialInRequest = false, - ContainsSecretMaterialInResponse = true - } - } - }, - { - InfisicalEndpointNames.RetrieveSecret, - new List - { - new InfisicalEndpointDefinition - { - Name = InfisicalEndpointNames.RetrieveSecret, - Resource = "Secrets", - Version = "v4", - Method = "GET", - Template = "/api/v4/secrets/{secretName}", - RequiresAuthorization = true, - ContainsSecretMaterialInRequest = false, - ContainsSecretMaterialInResponse = true - }, - new InfisicalEndpointDefinition - { - Name = InfisicalEndpointNames.RetrieveSecret, - Resource = "Secrets", - Version = "v3", - Method = "GET", - Template = "/api/v3/secrets/raw/{secretName}", - RequiresAuthorization = true, - ContainsSecretMaterialInRequest = false, - ContainsSecretMaterialInResponse = true - } - } - } - }; + list = new List(); + map[definition.Name] = list; + } + + list.Add(definition); + } + + private static void RegisterAuthentication(Dictionary> map) + { + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.UniversalAuthLogin, + Resource = "Authentication", + Version = "v1", + Method = "POST", + Template = "/api/v1/auth/universal-auth/login", + RequiresAuthorization = false, + ContainsSecretMaterialInRequest = true, + ContainsSecretMaterialInResponse = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.TokenAuthLogin, + Resource = "Authentication", + Version = "v1", + Method = "POST", + Template = "/api/v1/auth/token-auth/login", + RequiresAuthorization = false, + ContainsSecretMaterialInRequest = true, + ContainsSecretMaterialInResponse = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.JwtAuthLogin, + Resource = "Authentication", + Version = "v1", + Method = "POST", + Template = "/api/v1/auth/jwt-auth/login", + RequiresAuthorization = false, + ContainsSecretMaterialInRequest = true, + ContainsSecretMaterialInResponse = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.OidcAuthLogin, + Resource = "Authentication", + Version = "v1", + Method = "POST", + Template = "/api/v1/auth/oidc-auth/login", + RequiresAuthorization = false, + ContainsSecretMaterialInRequest = true, + ContainsSecretMaterialInResponse = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.LdapAuthLogin, + Resource = "Authentication", + Version = "v1", + Method = "POST", + Template = "/api/v1/auth/ldap-auth/login", + RequiresAuthorization = false, + ContainsSecretMaterialInRequest = true, + ContainsSecretMaterialInResponse = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.KubernetesAuthLogin, + Resource = "Authentication", + Version = "v1", + Method = "POST", + Template = "/api/v1/auth/kubernetes-auth/login", + RequiresAuthorization = false, + ContainsSecretMaterialInRequest = true, + ContainsSecretMaterialInResponse = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.AwsAuthLogin, + Resource = "Authentication", + Version = "v1", + Method = "POST", + Template = "/api/v1/auth/aws-auth/login", + RequiresAuthorization = false, + ContainsSecretMaterialInRequest = true, + ContainsSecretMaterialInResponse = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.AzureAuthLogin, + Resource = "Authentication", + Version = "v1", + Method = "POST", + Template = "/api/v1/auth/azure-auth/login", + RequiresAuthorization = false, + ContainsSecretMaterialInRequest = true, + ContainsSecretMaterialInResponse = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.GcpIamAuthLogin, + Resource = "Authentication", + Version = "v1", + Method = "POST", + Template = "/api/v1/auth/gcp-auth/login", + RequiresAuthorization = false, + ContainsSecretMaterialInRequest = true, + ContainsSecretMaterialInResponse = true + }); + } + + private static void RegisterSecrets(Dictionary> map) + { + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.ListSecrets, + Resource = "Secrets", + Version = "v4", + Method = "GET", + Template = "/api/v4/secrets", + RequiresAuthorization = true, + ContainsSecretMaterialInResponse = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.ListSecrets, + Resource = "Secrets", + Version = "v3", + Method = "GET", + Template = "/api/v3/secrets/raw", + RequiresAuthorization = true, + ContainsSecretMaterialInResponse = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.RetrieveSecret, + Resource = "Secrets", + Version = "v4", + Method = "GET", + Template = "/api/v4/secrets/{secretName}", + RequiresAuthorization = true, + ContainsSecretMaterialInResponse = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.RetrieveSecret, + Resource = "Secrets", + Version = "v3", + Method = "GET", + Template = "/api/v3/secrets/raw/{secretName}", + RequiresAuthorization = true, + ContainsSecretMaterialInResponse = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.CreateSecret, + Resource = "Secrets", + Version = "v3", + Method = "POST", + Template = "/api/v3/secrets/raw/{secretName}", + RequiresAuthorization = true, + ContainsSecretMaterialInRequest = true, + ContainsSecretMaterialInResponse = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.UpdateSecret, + Resource = "Secrets", + Version = "v3", + Method = "PATCH", + Template = "/api/v3/secrets/raw/{secretName}", + RequiresAuthorization = true, + ContainsSecretMaterialInRequest = true, + ContainsSecretMaterialInResponse = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.DeleteSecret, + Resource = "Secrets", + Version = "v3", + Method = "DELETE", + Template = "/api/v3/secrets/raw/{secretName}", + RequiresAuthorization = true, + ContainsSecretMaterialInResponse = true + }); + } + + private static void RegisterProjects(Dictionary> map) + { + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.ListProjects, + Resource = "Projects", + Version = "v1", + Method = "GET", + Template = "/api/v1/workspace", + RequiresAuthorization = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.RetrieveProject, + Resource = "Projects", + Version = "v1", + Method = "GET", + Template = "/api/v1/workspace/{projectId}", + RequiresAuthorization = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.CreateProject, + Resource = "Projects", + Version = "v2", + Method = "POST", + Template = "/api/v2/workspace", + RequiresAuthorization = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.UpdateProject, + Resource = "Projects", + Version = "v1", + Method = "PATCH", + Template = "/api/v1/workspace/{projectId}", + RequiresAuthorization = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.DeleteProject, + Resource = "Projects", + Version = "v1", + Method = "DELETE", + Template = "/api/v1/workspace/{projectId}", + RequiresAuthorization = true + }); + } + + private static void RegisterEnvironments(Dictionary> map) + { + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.ListEnvironments, + Resource = "Environments", + Version = "v1", + Method = "GET", + Template = "/api/v1/workspace/{projectId}", + RequiresAuthorization = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.RetrieveEnvironment, + Resource = "Environments", + Version = "v1", + Method = "GET", + Template = "/api/v1/workspace/{projectId}/environments/{environmentId}", + RequiresAuthorization = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.CreateEnvironment, + Resource = "Environments", + Version = "v1", + Method = "POST", + Template = "/api/v1/workspace/{projectId}/environments", + RequiresAuthorization = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.UpdateEnvironment, + Resource = "Environments", + Version = "v1", + Method = "PATCH", + Template = "/api/v1/workspace/{projectId}/environments/{environmentId}", + RequiresAuthorization = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.DeleteEnvironment, + Resource = "Environments", + Version = "v1", + Method = "DELETE", + Template = "/api/v1/workspace/{projectId}/environments/{environmentId}", + RequiresAuthorization = true + }); + } + + private static void RegisterFolders(Dictionary> map) + { + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.ListFolders, + Resource = "Folders", + Version = "v1", + Method = "GET", + Template = "/api/v1/folders", + RequiresAuthorization = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.RetrieveFolder, + Resource = "Folders", + Version = "v1", + Method = "GET", + Template = "/api/v1/folders/{folderId}", + RequiresAuthorization = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.CreateFolder, + Resource = "Folders", + Version = "v1", + Method = "POST", + Template = "/api/v1/folders", + RequiresAuthorization = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.UpdateFolder, + Resource = "Folders", + Version = "v1", + Method = "PATCH", + Template = "/api/v1/folders/{folderId}", + RequiresAuthorization = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.DeleteFolder, + Resource = "Folders", + Version = "v1", + Method = "DELETE", + Template = "/api/v1/folders/{folderId}", + RequiresAuthorization = true + }); + } + + private static void RegisterTags(Dictionary> map) + { + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.ListTags, + Resource = "Tags", + Version = "v1", + Method = "GET", + Template = "/api/v1/workspace/{projectId}/tags", + RequiresAuthorization = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.RetrieveTag, + Resource = "Tags", + Version = "v1", + Method = "GET", + Template = "/api/v1/workspace/{projectId}/tags/{tagId}", + RequiresAuthorization = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.CreateTag, + Resource = "Tags", + Version = "v1", + Method = "POST", + Template = "/api/v1/workspace/{projectId}/tags", + RequiresAuthorization = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.UpdateTag, + Resource = "Tags", + Version = "v1", + Method = "PATCH", + Template = "/api/v1/workspace/{projectId}/tags/{tagId}", + RequiresAuthorization = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.DeleteTag, + Resource = "Tags", + Version = "v1", + Method = "DELETE", + Template = "/api/v1/workspace/{projectId}/tags/{tagId}", + RequiresAuthorization = true + }); + } public static InfisicalEndpointDefinition Get(string name) { diff --git a/src/PSInfisicalAPI/Http/InfisicalApiInvoker.cs b/src/PSInfisicalAPI/Http/InfisicalApiInvoker.cs new file mode 100644 index 0000000..d6e2872 --- /dev/null +++ b/src/PSInfisicalAPI/Http/InfisicalApiInvoker.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Endpoints; +using PSInfisicalAPI.Errors; +using PSInfisicalAPI.Security; + +namespace PSInfisicalAPI.Http +{ + internal sealed class InfisicalApiInvoker + { + private readonly IInfisicalHttpClient _httpClient; + + public InfisicalApiInvoker(IInfisicalHttpClient httpClient) + { + _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); + } + + public InfisicalHttpResponse Invoke( + InfisicalConnection connection, + string endpointName, + string operationName, + IDictionary pathParameters, + IEnumerable> queryParameters, + string body) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + if (string.IsNullOrEmpty(endpointName)) { throw new ArgumentNullException(nameof(endpointName)); } + + InfisicalEndpointDefinition definition = InfisicalEndpointRegistry.Get(endpointName); + Uri uri = InfisicalUriBuilder.Build(connection.BaseUri, definition, pathParameters, queryParameters); + + InfisicalHttpResponse response = ExecuteAuthorized(connection, definition, operationName, uri, body); + + if (response.StatusCode >= 200 && response.StatusCode < 300) + { + return response; + } + + InfisicalApiException exception = BuildApiException(response, definition); + response.Clear(); + throw exception; + } + + private InfisicalHttpResponse ExecuteAuthorized( + InfisicalConnection connection, + InfisicalEndpointDefinition definition, + string operationName, + Uri uri, + string body) + { + Dictionary headers = new Dictionary(StringComparer.OrdinalIgnoreCase); + headers["Accept"] = "application/json"; + + if (!string.IsNullOrEmpty(body)) + { + headers["Content-Type"] = "application/json"; + } + + if (definition.RequiresAuthorization) + { + if (connection.AccessToken == null) + { + throw new InfisicalAuthenticationException("Connection does not contain an access token."); + } + + SecureStringUtility.UsePlainText(connection.AccessToken, plainToken => + { + headers["Authorization"] = string.Concat("Bearer ", plainToken ?? string.Empty); + }); + } + + InfisicalHttpRequest request = new InfisicalHttpRequest + { + OperationName = operationName, + EndpointName = definition.Name, + Method = definition.Method, + Uri = uri, + Headers = headers, + Body = body, + ContainsSecretMaterialInRequest = definition.ContainsSecretMaterialInRequest, + ContainsSecretMaterialInResponse = definition.ContainsSecretMaterialInResponse + }; + + return _httpClient.Send(request); + } + + private static InfisicalApiException BuildApiException(InfisicalHttpResponse response, InfisicalEndpointDefinition definition) + { + InfisicalApiException exception = new InfisicalApiException(string.Concat( + "Infisical API returned ", + response.StatusCode.ToString(CultureInfo.InvariantCulture), + " (", response.ReasonPhrase ?? string.Empty, ").")); + exception.StatusCode = response.StatusCode; + exception.ReasonPhrase = response.ReasonPhrase; + exception.EndpointName = definition.Name; + exception.RequestMethod = definition.Method; + exception.SanitizedBody = response.Body; + return exception; + } + } +} -- 2.52.0 From 0ebacddb2c23b3c31f5d68e40e6dcd1810922888 Mon Sep 17 00:00:00 2001 From: GraceSolutions Date: Wed, 3 Jun 2026 17:21:02 -0400 Subject: [PATCH 03/12] M2: Projects CRUD - model, DTOs, mapper, client, 5 cmdlets + tests --- build.ps1 | 9 +- .../ProjectMapperTests.cs | 108 ++++++++++++ .../Cmdlets/GetInfisicalProjectCmdlet.cs | 35 ++++ .../Cmdlets/GetInfisicalProjectsCmdlet.cs | 32 ++++ .../Cmdlets/NewInfisicalProjectCmdlet.cs | 47 +++++ .../Cmdlets/RemoveInfisicalProjectCmdlet.cs | 41 +++++ .../Cmdlets/UpdateInfisicalProjectCmdlet.cs | 44 +++++ src/PSInfisicalAPI/Models/InfisicalProject.cs | 23 +++ .../Projects/InfisicalProjectClient.cs | 162 ++++++++++++++++++ .../Projects/InfisicalProjectDtos.cs | 56 ++++++ .../Projects/InfisicalProjectMapper.cs | 89 ++++++++++ 11 files changed, 644 insertions(+), 2 deletions(-) create mode 100644 src/PSInfisicalAPI.Tests/ProjectMapperTests.cs create mode 100644 src/PSInfisicalAPI/Cmdlets/GetInfisicalProjectCmdlet.cs create mode 100644 src/PSInfisicalAPI/Cmdlets/GetInfisicalProjectsCmdlet.cs create mode 100644 src/PSInfisicalAPI/Cmdlets/NewInfisicalProjectCmdlet.cs create mode 100644 src/PSInfisicalAPI/Cmdlets/RemoveInfisicalProjectCmdlet.cs create mode 100644 src/PSInfisicalAPI/Cmdlets/UpdateInfisicalProjectCmdlet.cs create mode 100644 src/PSInfisicalAPI/Models/InfisicalProject.cs create mode 100644 src/PSInfisicalAPI/Projects/InfisicalProjectClient.cs create mode 100644 src/PSInfisicalAPI/Projects/InfisicalProjectDtos.cs create mode 100644 src/PSInfisicalAPI/Projects/InfisicalProjectMapper.cs diff --git a/build.ps1 b/build.ps1 index ef7a856..8171355 100644 --- a/build.ps1 +++ b/build.ps1 @@ -103,7 +103,12 @@ function Write-Manifest { 'Get-InfisicalSecrets', 'Get-InfisicalSecret', 'ConvertTo-InfisicalSecretDictionary', - 'Export-InfisicalSecrets' + 'Export-InfisicalSecrets', + 'Get-InfisicalProjects', + 'Get-InfisicalProject', + 'New-InfisicalProject', + 'Update-InfisicalProject', + 'Remove-InfisicalProject' ) AliasesToExport = @() VariablesToExport = @() @@ -163,7 +168,7 @@ if (`$null -eq `$manifest) { Import-Module -Name '$($ModuleDirectory.FullName)' -Force -`$cmds = @('Connect-Infisical','Disconnect-Infisical','Get-InfisicalSecrets','Get-InfisicalSecret','ConvertTo-InfisicalSecretDictionary','Export-InfisicalSecrets') +`$cmds = @('Connect-Infisical','Disconnect-Infisical','Get-InfisicalSecrets','Get-InfisicalSecret','ConvertTo-InfisicalSecretDictionary','Export-InfisicalSecrets','Get-InfisicalProjects','Get-InfisicalProject','New-InfisicalProject','Update-InfisicalProject','Remove-InfisicalProject') foreach (`$c in `$cmds) { if (-not (Get-Command -Name `$c -Module PSInfisicalAPI -ErrorAction SilentlyContinue)) { throw "Cmdlet not found: `$c" diff --git a/src/PSInfisicalAPI.Tests/ProjectMapperTests.cs b/src/PSInfisicalAPI.Tests/ProjectMapperTests.cs new file mode 100644 index 0000000..5c02718 --- /dev/null +++ b/src/PSInfisicalAPI.Tests/ProjectMapperTests.cs @@ -0,0 +1,108 @@ +using System.Collections; +using System.Reflection; +using PSInfisicalAPI.Models; +using Xunit; + +namespace PSInfisicalAPI.Tests +{ + public class ProjectMapperTests + { + private static readonly System.Type MapperType = typeof(PSInfisicalAPI.Connections.InfisicalConnection).Assembly + .GetType("PSInfisicalAPI.Projects.InfisicalProjectMapper", true); + + private static readonly System.Type DtoType = typeof(PSInfisicalAPI.Connections.InfisicalConnection).Assembly + .GetType("PSInfisicalAPI.Projects.InfisicalProjectResponseDto", true); + + private static readonly System.Type EnvDtoType = typeof(PSInfisicalAPI.Connections.InfisicalConnection).Assembly + .GetType("PSInfisicalAPI.Projects.InfisicalProjectEnvironmentDto", true); + + private static InfisicalProject InvokeMap(object dto) + { + MethodInfo map = MapperType.GetMethod("Map", BindingFlags.Public | BindingFlags.Static); + return (InfisicalProject)map.Invoke(null, new[] { dto }); + } + + [Fact] + public void Map_Null_Dto_Returns_Null() + { + Assert.Null(InvokeMap(null)); + } + + [Fact] + public void Map_Populates_Core_Fields() + { + object dto = System.Activator.CreateInstance(DtoType); + DtoType.GetProperty("Id").SetValue(dto, "proj-001"); + DtoType.GetProperty("Name").SetValue(dto, "DevOps"); + DtoType.GetProperty("Slug").SetValue(dto, "devops"); + DtoType.GetProperty("Description").SetValue(dto, "Internal DevOps project"); + DtoType.GetProperty("Organization").SetValue(dto, "org-abc"); + DtoType.GetProperty("Type").SetValue(dto, "secret-manager"); + DtoType.GetProperty("AutoCapitalization").SetValue(dto, true); + DtoType.GetProperty("CreatedAt").SetValue(dto, "2026-01-15T12:34:56Z"); + DtoType.GetProperty("UpdatedAt").SetValue(dto, "2026-02-20T09:00:00Z"); + + InfisicalProject project = InvokeMap(dto); + + Assert.Equal("proj-001", project.Id); + Assert.Equal("DevOps", project.Name); + Assert.Equal("devops", project.Slug); + Assert.Equal("Internal DevOps project", project.Description); + Assert.Equal("org-abc", project.OrganizationId); + Assert.Equal("secret-manager", project.Type); + Assert.True(project.AutoCapitalization); + Assert.NotNull(project.CreatedAtUtc); + Assert.NotNull(project.UpdatedAtUtc); + Assert.Empty(project.EnvironmentSlugs); + } + + [Fact] + public void Map_Falls_Back_To_InternalId_And_OrgId() + { + object dto = System.Activator.CreateInstance(DtoType); + DtoType.GetProperty("InternalId").SetValue(dto, "internal-id-1"); + DtoType.GetProperty("OrgId").SetValue(dto, "org-fallback"); + + InfisicalProject project = InvokeMap(dto); + + Assert.Equal("internal-id-1", project.Id); + Assert.Equal("org-fallback", project.OrganizationId); + } + + [Fact] + public void Map_Extracts_EnvironmentSlugs() + { + object dto = System.Activator.CreateInstance(DtoType); + DtoType.GetProperty("Id").SetValue(dto, "proj-002"); + + System.Type listType = typeof(System.Collections.Generic.List<>).MakeGenericType(EnvDtoType); + IList envs = (IList)System.Activator.CreateInstance(listType); + + object env1 = System.Activator.CreateInstance(EnvDtoType); + EnvDtoType.GetProperty("Slug").SetValue(env1, "dev"); + EnvDtoType.GetProperty("Name").SetValue(env1, "Development"); + envs.Add(env1); + + object env2 = System.Activator.CreateInstance(EnvDtoType); + EnvDtoType.GetProperty("Slug").SetValue(env2, "prod"); + envs.Add(env2); + + DtoType.GetProperty("Environments").SetValue(dto, envs); + + InfisicalProject project = InvokeMap(dto); + + Assert.Equal(2, project.EnvironmentSlugs.Length); + Assert.Contains("dev", project.EnvironmentSlugs); + Assert.Contains("prod", project.EnvironmentSlugs); + } + + [Fact] + public void MapMany_Null_Returns_Empty() + { + MethodInfo mapMany = MapperType.GetMethod("MapMany", BindingFlags.Public | BindingFlags.Static); + InfisicalProject[] result = (InfisicalProject[])mapMany.Invoke(null, new object[] { null }); + Assert.NotNull(result); + Assert.Empty(result); + } + } +} diff --git a/src/PSInfisicalAPI/Cmdlets/GetInfisicalProjectCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/GetInfisicalProjectCmdlet.cs new file mode 100644 index 0000000..979928d --- /dev/null +++ b/src/PSInfisicalAPI/Cmdlets/GetInfisicalProjectCmdlet.cs @@ -0,0 +1,35 @@ +using System; +using System.Management.Automation; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Models; +using PSInfisicalAPI.Projects; + +namespace PSInfisicalAPI.Cmdlets +{ + [Cmdlet(VerbsCommon.Get, "InfisicalProject")] + [OutputType(typeof(InfisicalProject))] + public sealed class GetInfisicalProjectCmdlet : InfisicalCmdletBase + { + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)] + [Alias("Id")] + public string ProjectId { get; set; } + + protected override void ProcessRecord() + { + try + { + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + InfisicalProjectClient client = new InfisicalProjectClient(HttpClient, Logger); + InfisicalProject project = client.Retrieve(connection, ProjectId); + if (project != null) + { + WriteObject(project); + } + } + catch (Exception exception) + { + ThrowTerminatingForException("GetInfisicalProjectCmdlet", "RetrieveProject", exception); + } + } + } +} diff --git a/src/PSInfisicalAPI/Cmdlets/GetInfisicalProjectsCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/GetInfisicalProjectsCmdlet.cs new file mode 100644 index 0000000..fa6150a --- /dev/null +++ b/src/PSInfisicalAPI/Cmdlets/GetInfisicalProjectsCmdlet.cs @@ -0,0 +1,32 @@ +using System; +using System.Management.Automation; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Models; +using PSInfisicalAPI.Projects; + +namespace PSInfisicalAPI.Cmdlets +{ + [Cmdlet(VerbsCommon.Get, "InfisicalProjects")] + [OutputType(typeof(InfisicalProject))] + public sealed class GetInfisicalProjectsCmdlet : InfisicalCmdletBase + { + protected override void ProcessRecord() + { + try + { + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + InfisicalProjectClient client = new InfisicalProjectClient(HttpClient, Logger); + InfisicalProject[] projects = client.List(connection); + + foreach (InfisicalProject project in projects) + { + WriteObject(project); + } + } + catch (Exception exception) + { + ThrowTerminatingForException("GetInfisicalProjectsCmdlet", "ListProjects", exception); + } + } + } +} diff --git a/src/PSInfisicalAPI/Cmdlets/NewInfisicalProjectCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/NewInfisicalProjectCmdlet.cs new file mode 100644 index 0000000..73cd932 --- /dev/null +++ b/src/PSInfisicalAPI/Cmdlets/NewInfisicalProjectCmdlet.cs @@ -0,0 +1,47 @@ +using System; +using System.Management.Automation; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Models; +using PSInfisicalAPI.Projects; + +namespace PSInfisicalAPI.Cmdlets +{ + [Cmdlet(VerbsCommon.New, "InfisicalProject", SupportsShouldProcess = true)] + [OutputType(typeof(InfisicalProject))] + public sealed class NewInfisicalProjectCmdlet : InfisicalCmdletBase + { + [Parameter(Mandatory = true, Position = 0)] + [Alias("Name")] + public string ProjectName { get; set; } + + [Parameter] public string Slug { get; set; } + [Parameter] public string Description { get; set; } + [Parameter] public string Type { get; set; } + [Parameter] public string OrganizationId { get; set; } + + protected override void ProcessRecord() + { + try + { + if (!ShouldProcess(ProjectName, "Create Infisical project")) + { + return; + } + + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + string resolvedOrgId = !string.IsNullOrEmpty(OrganizationId) ? OrganizationId : connection.OrganizationId; + + InfisicalProjectClient client = new InfisicalProjectClient(HttpClient, Logger); + InfisicalProject project = client.Create(connection, ProjectName, Slug, Description, Type, resolvedOrgId); + if (project != null) + { + WriteObject(project); + } + } + catch (Exception exception) + { + ThrowTerminatingForException("NewInfisicalProjectCmdlet", "CreateProject", exception); + } + } + } +} diff --git a/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalProjectCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalProjectCmdlet.cs new file mode 100644 index 0000000..6cd89e1 --- /dev/null +++ b/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalProjectCmdlet.cs @@ -0,0 +1,41 @@ +using System; +using System.Management.Automation; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Projects; + +namespace PSInfisicalAPI.Cmdlets +{ + [Cmdlet(VerbsCommon.Remove, "InfisicalProject", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)] + public sealed class RemoveInfisicalProjectCmdlet : InfisicalCmdletBase + { + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)] + [Alias("Id")] + public string ProjectId { get; set; } + + [Parameter] public SwitchParameter PassThru { get; set; } + + protected override void ProcessRecord() + { + try + { + if (!ShouldProcess(ProjectId, "Remove Infisical project")) + { + return; + } + + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + InfisicalProjectClient client = new InfisicalProjectClient(HttpClient, Logger); + client.Delete(connection, ProjectId); + + if (PassThru.IsPresent) + { + WriteObject(ProjectId); + } + } + catch (Exception exception) + { + ThrowTerminatingForException("RemoveInfisicalProjectCmdlet", "DeleteProject", exception); + } + } + } +} diff --git a/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalProjectCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalProjectCmdlet.cs new file mode 100644 index 0000000..3a58b3f --- /dev/null +++ b/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalProjectCmdlet.cs @@ -0,0 +1,44 @@ +using System; +using System.Management.Automation; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Models; +using PSInfisicalAPI.Projects; + +namespace PSInfisicalAPI.Cmdlets +{ + [Cmdlet(VerbsData.Update, "InfisicalProject", SupportsShouldProcess = true)] + [OutputType(typeof(InfisicalProject))] + public sealed class UpdateInfisicalProjectCmdlet : InfisicalCmdletBase + { + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)] + [Alias("Id")] + public string ProjectId { get; set; } + + [Parameter] public string Name { get; set; } + [Parameter] public string Description { get; set; } + [Parameter] public bool? AutoCapitalization { get; set; } + + protected override void ProcessRecord() + { + try + { + if (!ShouldProcess(ProjectId, "Update Infisical project")) + { + return; + } + + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + InfisicalProjectClient client = new InfisicalProjectClient(HttpClient, Logger); + InfisicalProject project = client.Update(connection, ProjectId, Name, Description, AutoCapitalization); + if (project != null) + { + WriteObject(project); + } + } + catch (Exception exception) + { + ThrowTerminatingForException("UpdateInfisicalProjectCmdlet", "UpdateProject", exception); + } + } + } +} diff --git a/src/PSInfisicalAPI/Models/InfisicalProject.cs b/src/PSInfisicalAPI/Models/InfisicalProject.cs new file mode 100644 index 0000000..c4ddc2d --- /dev/null +++ b/src/PSInfisicalAPI/Models/InfisicalProject.cs @@ -0,0 +1,23 @@ +using System; + +namespace PSInfisicalAPI.Models +{ + public sealed class InfisicalProject + { + public string Id { get; set; } + public string Name { get; set; } + public string Slug { get; set; } + public string Description { get; set; } + public string OrganizationId { get; set; } + public string Type { get; set; } + public bool AutoCapitalization { get; set; } + public string[] EnvironmentSlugs { get; set; } + public DateTimeOffset? CreatedAtUtc { get; set; } + public DateTimeOffset? UpdatedAtUtc { get; set; } + + public override string ToString() + { + return string.IsNullOrEmpty(Slug) ? (Name ?? Id) : Slug; + } + } +} diff --git a/src/PSInfisicalAPI/Projects/InfisicalProjectClient.cs b/src/PSInfisicalAPI/Projects/InfisicalProjectClient.cs new file mode 100644 index 0000000..a0c5e28 --- /dev/null +++ b/src/PSInfisicalAPI/Projects/InfisicalProjectClient.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections.Generic; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Endpoints; +using PSInfisicalAPI.Errors; +using PSInfisicalAPI.Http; +using PSInfisicalAPI.Logging; +using PSInfisicalAPI.Models; +using PSInfisicalAPI.Serialization; + +namespace PSInfisicalAPI.Projects +{ + public sealed class InfisicalProjectClient + { + private const string Component = "ProjectClient"; + + private readonly IInfisicalLogger _logger; + private readonly JsonInfisicalSerializer _serializer; + private readonly InfisicalApiInvoker _invoker; + + public InfisicalProjectClient(IInfisicalHttpClient httpClient, IInfisicalLogger logger) + { + if (httpClient == null) { throw new ArgumentNullException(nameof(httpClient)); } + _logger = logger ?? NullInfisicalLogger.Instance; + _serializer = new JsonInfisicalSerializer(); + _invoker = new InfisicalApiInvoker(httpClient); + } + + public InfisicalProject[] List(InfisicalConnection connection) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + + try + { + _logger.Information(Component, "Attempting to list Infisical projects. Please Wait..."); + InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.ListProjects, "ListProjects", null, null, null); + InfisicalProjectListResponseDto dto = _serializer.Deserialize(response.Body); + response.Clear(); + + List source = (dto != null && dto.Workspaces != null) ? dto.Workspaces : (dto != null ? dto.Projects : null); + InfisicalProject[] mapped = InfisicalProjectMapper.MapMany(source); + _logger.Information(Component, "Infisical project list retrieval was successful."); + return mapped; + } + catch (Exception) + { + _logger.Error(Component, "Infisical project list retrieval failed."); + throw; + } + } + + public InfisicalProject Retrieve(InfisicalConnection connection, string projectId) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + if (string.IsNullOrEmpty(projectId)) { throw new InfisicalConfigurationException("ProjectId is required."); } + + Dictionary pathParameters = new Dictionary { { "projectId", projectId } }; + + try + { + _logger.Information(Component, string.Concat("Attempting to retrieve Infisical project '", projectId, "'. Please Wait...")); + InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.RetrieveProject, "RetrieveProject", pathParameters, null, null); + InfisicalProjectSingleResponseDto dto = _serializer.Deserialize(response.Body); + response.Clear(); + + InfisicalProjectResponseDto inner = dto != null ? (dto.Workspace ?? dto.Project) : null; + InfisicalProject mapped = InfisicalProjectMapper.Map(inner); + _logger.Information(Component, "Infisical project retrieval was successful."); + return mapped; + } + catch (Exception) + { + _logger.Error(Component, "Infisical project retrieval failed."); + throw; + } + } + + public InfisicalProject Create(InfisicalConnection connection, string projectName, string slug, string description, string type, string organizationId) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + if (string.IsNullOrEmpty(projectName)) { throw new InfisicalConfigurationException("ProjectName is required."); } + + InfisicalProjectCreateRequestDto request = new InfisicalProjectCreateRequestDto + { + ProjectName = projectName, + Slug = slug, + ProjectDescription = description, + Type = type, + OrganizationId = organizationId + }; + + string body = _serializer.Serialize(request); + + try + { + _logger.Information(Component, string.Concat("Attempting to create Infisical project '", projectName, "'. Please Wait...")); + InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.CreateProject, "CreateProject", null, null, body); + InfisicalProjectSingleResponseDto dto = _serializer.Deserialize(response.Body); + response.Clear(); + + InfisicalProjectResponseDto inner = dto != null ? (dto.Project ?? dto.Workspace) : null; + InfisicalProject mapped = InfisicalProjectMapper.Map(inner); + _logger.Information(Component, "Infisical project creation was successful."); + return mapped; + } + catch (Exception) + { + _logger.Error(Component, "Infisical project creation failed."); + throw; + } + } + + public InfisicalProject Update(InfisicalConnection connection, string projectId, string name, string description, bool? autoCapitalization) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + if (string.IsNullOrEmpty(projectId)) { throw new InfisicalConfigurationException("ProjectId is required."); } + + Dictionary pathParameters = new Dictionary { { "projectId", projectId } }; + InfisicalProjectUpdateRequestDto request = new InfisicalProjectUpdateRequestDto { Name = name, Description = description, AutoCapitalization = autoCapitalization }; + string body = _serializer.Serialize(request); + + try + { + _logger.Information(Component, string.Concat("Attempting to update Infisical project '", projectId, "'. Please Wait...")); + InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.UpdateProject, "UpdateProject", pathParameters, null, body); + InfisicalProjectSingleResponseDto dto = _serializer.Deserialize(response.Body); + response.Clear(); + + InfisicalProjectResponseDto inner = dto != null ? (dto.Workspace ?? dto.Project) : null; + InfisicalProject mapped = InfisicalProjectMapper.Map(inner); + _logger.Information(Component, "Infisical project update was successful."); + return mapped; + } + catch (Exception) + { + _logger.Error(Component, "Infisical project update failed."); + throw; + } + } + + public void Delete(InfisicalConnection connection, string projectId) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + if (string.IsNullOrEmpty(projectId)) { throw new InfisicalConfigurationException("ProjectId is required."); } + + Dictionary pathParameters = new Dictionary { { "projectId", projectId } }; + + try + { + _logger.Information(Component, string.Concat("Attempting to delete Infisical project '", projectId, "'. Please Wait...")); + InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.DeleteProject, "DeleteProject", pathParameters, null, null); + response.Clear(); + _logger.Information(Component, "Infisical project deletion was successful."); + } + catch (Exception) + { + _logger.Error(Component, "Infisical project deletion failed."); + throw; + } + } + } +} diff --git a/src/PSInfisicalAPI/Projects/InfisicalProjectDtos.cs b/src/PSInfisicalAPI/Projects/InfisicalProjectDtos.cs new file mode 100644 index 0000000..f6f6757 --- /dev/null +++ b/src/PSInfisicalAPI/Projects/InfisicalProjectDtos.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace PSInfisicalAPI.Projects +{ + internal sealed class InfisicalProjectResponseDto + { + [JsonProperty("id")] public string Id { get; set; } + [JsonProperty("_id")] public string InternalId { get; set; } + [JsonProperty("name")] public string Name { get; set; } + [JsonProperty("slug")] public string Slug { get; set; } + [JsonProperty("description")] public string Description { get; set; } + [JsonProperty("organization")] public string Organization { get; set; } + [JsonProperty("orgId")] public string OrgId { get; set; } + [JsonProperty("type")] public string Type { get; set; } + [JsonProperty("autoCapitalization")] public bool AutoCapitalization { get; set; } + [JsonProperty("createdAt")] public string CreatedAt { get; set; } + [JsonProperty("updatedAt")] public string UpdatedAt { get; set; } + [JsonProperty("environments")] public List Environments { get; set; } + } + + internal sealed class InfisicalProjectEnvironmentDto + { + [JsonProperty("id")] public string Id { get; set; } + [JsonProperty("name")] public string Name { get; set; } + [JsonProperty("slug")] public string Slug { get; set; } + } + + internal sealed class InfisicalProjectListResponseDto + { + [JsonProperty("workspaces")] public List Workspaces { get; set; } + [JsonProperty("projects")] public List Projects { get; set; } + } + + internal sealed class InfisicalProjectSingleResponseDto + { + [JsonProperty("workspace")] public InfisicalProjectResponseDto Workspace { get; set; } + [JsonProperty("project")] public InfisicalProjectResponseDto Project { get; set; } + } + + internal sealed class InfisicalProjectCreateRequestDto + { + [JsonProperty("projectName")] public string ProjectName { get; set; } + [JsonProperty("slug", NullValueHandling = NullValueHandling.Ignore)] public string Slug { get; set; } + [JsonProperty("projectDescription", NullValueHandling = NullValueHandling.Ignore)] public string ProjectDescription { get; set; } + [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)] public string Type { get; set; } + [JsonProperty("organizationId", NullValueHandling = NullValueHandling.Ignore)] public string OrganizationId { get; set; } + } + + internal sealed class InfisicalProjectUpdateRequestDto + { + [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)] public string Name { get; set; } + [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)] public string Description { get; set; } + [JsonProperty("autoCapitalization", NullValueHandling = NullValueHandling.Ignore)] public bool? AutoCapitalization { get; set; } + } +} diff --git a/src/PSInfisicalAPI/Projects/InfisicalProjectMapper.cs b/src/PSInfisicalAPI/Projects/InfisicalProjectMapper.cs new file mode 100644 index 0000000..3821b1b --- /dev/null +++ b/src/PSInfisicalAPI/Projects/InfisicalProjectMapper.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using PSInfisicalAPI.Models; + +namespace PSInfisicalAPI.Projects +{ + internal static class InfisicalProjectMapper + { + public static InfisicalProject Map(InfisicalProjectResponseDto dto) + { + if (dto == null) + { + return null; + } + + InfisicalProject project = new InfisicalProject + { + Id = !string.IsNullOrEmpty(dto.Id) ? dto.Id : dto.InternalId, + Name = dto.Name, + Slug = dto.Slug, + Description = dto.Description, + OrganizationId = !string.IsNullOrEmpty(dto.Organization) ? dto.Organization : dto.OrgId, + Type = dto.Type, + AutoCapitalization = dto.AutoCapitalization, + EnvironmentSlugs = MapEnvironmentSlugs(dto.Environments), + CreatedAtUtc = ParseTimestamp(dto.CreatedAt), + UpdatedAtUtc = ParseTimestamp(dto.UpdatedAt) + }; + + return project; + } + + public static InfisicalProject[] MapMany(IEnumerable items) + { + if (items == null) + { + return Array.Empty(); + } + + List results = new List(); + foreach (InfisicalProjectResponseDto dto in items) + { + InfisicalProject mapped = Map(dto); + if (mapped != null) + { + results.Add(mapped); + } + } + + return results.ToArray(); + } + + private static string[] MapEnvironmentSlugs(List environments) + { + if (environments == null || environments.Count == 0) + { + return Array.Empty(); + } + + List slugs = new List(environments.Count); + foreach (InfisicalProjectEnvironmentDto env in environments) + { + if (env != null && !string.IsNullOrEmpty(env.Slug)) + { + slugs.Add(env.Slug); + } + } + + return slugs.ToArray(); + } + + 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; + } + } +} -- 2.52.0 From 6eab0713b53c3726528e201c3f8c8f4df8bd8560 Mon Sep 17 00:00:00 2001 From: GraceSolutions Date: Wed, 3 Jun 2026 17:23:11 -0400 Subject: [PATCH 04/12] M3: Environments CRUD - model, DTOs, mapper, client, 5 cmdlets + tests --- build.ps1 | 9 +- .../EnvironmentMapperTests.cs | 77 ++++++++ .../Cmdlets/GetInfisicalEnvironmentCmdlet.cs | 37 ++++ .../Cmdlets/GetInfisicalEnvironmentsCmdlet.cs | 33 ++++ .../Cmdlets/NewInfisicalEnvironmentCmdlet.cs | 41 +++++ .../RemoveInfisicalEnvironmentCmdlet.cs | 42 +++++ .../UpdateInfisicalEnvironmentCmdlet.cs | 45 +++++ .../InfisicalEnvironmentClient.cs | 166 ++++++++++++++++++ .../Environments/InfisicalEnvironmentDtos.cs | 47 +++++ .../InfisicalEnvironmentMapper.cs | 50 ++++++ .../Models/InfisicalEnvironment.cs | 16 ++ 11 files changed, 561 insertions(+), 2 deletions(-) create mode 100644 src/PSInfisicalAPI.Tests/EnvironmentMapperTests.cs create mode 100644 src/PSInfisicalAPI/Cmdlets/GetInfisicalEnvironmentCmdlet.cs create mode 100644 src/PSInfisicalAPI/Cmdlets/GetInfisicalEnvironmentsCmdlet.cs create mode 100644 src/PSInfisicalAPI/Cmdlets/NewInfisicalEnvironmentCmdlet.cs create mode 100644 src/PSInfisicalAPI/Cmdlets/RemoveInfisicalEnvironmentCmdlet.cs create mode 100644 src/PSInfisicalAPI/Cmdlets/UpdateInfisicalEnvironmentCmdlet.cs create mode 100644 src/PSInfisicalAPI/Environments/InfisicalEnvironmentClient.cs create mode 100644 src/PSInfisicalAPI/Environments/InfisicalEnvironmentDtos.cs create mode 100644 src/PSInfisicalAPI/Environments/InfisicalEnvironmentMapper.cs create mode 100644 src/PSInfisicalAPI/Models/InfisicalEnvironment.cs diff --git a/build.ps1 b/build.ps1 index 8171355..7ba58a5 100644 --- a/build.ps1 +++ b/build.ps1 @@ -108,7 +108,12 @@ function Write-Manifest { 'Get-InfisicalProject', 'New-InfisicalProject', 'Update-InfisicalProject', - 'Remove-InfisicalProject' + 'Remove-InfisicalProject', + 'Get-InfisicalEnvironments', + 'Get-InfisicalEnvironment', + 'New-InfisicalEnvironment', + 'Update-InfisicalEnvironment', + 'Remove-InfisicalEnvironment' ) AliasesToExport = @() VariablesToExport = @() @@ -168,7 +173,7 @@ if (`$null -eq `$manifest) { Import-Module -Name '$($ModuleDirectory.FullName)' -Force -`$cmds = @('Connect-Infisical','Disconnect-Infisical','Get-InfisicalSecrets','Get-InfisicalSecret','ConvertTo-InfisicalSecretDictionary','Export-InfisicalSecrets','Get-InfisicalProjects','Get-InfisicalProject','New-InfisicalProject','Update-InfisicalProject','Remove-InfisicalProject') +`$cmds = @('Connect-Infisical','Disconnect-Infisical','Get-InfisicalSecrets','Get-InfisicalSecret','ConvertTo-InfisicalSecretDictionary','Export-InfisicalSecrets','Get-InfisicalProjects','Get-InfisicalProject','New-InfisicalProject','Update-InfisicalProject','Remove-InfisicalProject','Get-InfisicalEnvironments','Get-InfisicalEnvironment','New-InfisicalEnvironment','Update-InfisicalEnvironment','Remove-InfisicalEnvironment') foreach (`$c in `$cmds) { if (-not (Get-Command -Name `$c -Module PSInfisicalAPI -ErrorAction SilentlyContinue)) { throw "Cmdlet not found: `$c" diff --git a/src/PSInfisicalAPI.Tests/EnvironmentMapperTests.cs b/src/PSInfisicalAPI.Tests/EnvironmentMapperTests.cs new file mode 100644 index 0000000..302daae --- /dev/null +++ b/src/PSInfisicalAPI.Tests/EnvironmentMapperTests.cs @@ -0,0 +1,77 @@ +using System.Reflection; +using PSInfisicalAPI.Models; +using Xunit; + +namespace PSInfisicalAPI.Tests +{ + public class EnvironmentMapperTests + { + private static readonly System.Type MapperType = typeof(PSInfisicalAPI.Connections.InfisicalConnection).Assembly + .GetType("PSInfisicalAPI.Environments.InfisicalEnvironmentMapper", true); + + private static readonly System.Type DtoType = typeof(PSInfisicalAPI.Connections.InfisicalConnection).Assembly + .GetType("PSInfisicalAPI.Environments.InfisicalEnvironmentResponseDto", true); + + private static InfisicalEnvironment InvokeMap(object dto, string fallbackProjectId) + { + MethodInfo map = MapperType.GetMethod("Map", BindingFlags.Public | BindingFlags.Static); + return (InfisicalEnvironment)map.Invoke(null, new object[] { dto, fallbackProjectId }); + } + + [Fact] + public void Map_Null_Returns_Null() + { + Assert.Null(InvokeMap(null, "proj-x")); + } + + [Fact] + public void Map_Populates_Fields_With_Explicit_ProjectId() + { + object dto = System.Activator.CreateInstance(DtoType); + DtoType.GetProperty("Id").SetValue(dto, "env-001"); + DtoType.GetProperty("Name").SetValue(dto, "Production"); + DtoType.GetProperty("Slug").SetValue(dto, "prod"); + DtoType.GetProperty("Position").SetValue(dto, 1); + DtoType.GetProperty("ProjectId").SetValue(dto, "proj-001"); + + InfisicalEnvironment env = InvokeMap(dto, "fallback-proj"); + + Assert.Equal("env-001", env.Id); + Assert.Equal("Production", env.Name); + Assert.Equal("prod", env.Slug); + Assert.Equal(1, env.Position); + Assert.Equal("proj-001", env.ProjectId); + } + + [Fact] + public void Map_Uses_WorkspaceId_When_ProjectId_Empty() + { + object dto = System.Activator.CreateInstance(DtoType); + DtoType.GetProperty("Id").SetValue(dto, "env-002"); + DtoType.GetProperty("WorkspaceId").SetValue(dto, "wks-002"); + + InfisicalEnvironment env = InvokeMap(dto, "fallback-proj"); + Assert.Equal("wks-002", env.ProjectId); + } + + [Fact] + public void Map_Uses_Fallback_When_No_ProjectId_Or_WorkspaceId() + { + object dto = System.Activator.CreateInstance(DtoType); + DtoType.GetProperty("Id").SetValue(dto, "env-003"); + + InfisicalEnvironment env = InvokeMap(dto, "fallback-proj"); + Assert.Equal("fallback-proj", env.ProjectId); + } + + [Fact] + public void Map_Falls_Back_To_InternalId_For_Id() + { + object dto = System.Activator.CreateInstance(DtoType); + DtoType.GetProperty("InternalId").SetValue(dto, "internal-env"); + + InfisicalEnvironment env = InvokeMap(dto, "p"); + Assert.Equal("internal-env", env.Id); + } + } +} diff --git a/src/PSInfisicalAPI/Cmdlets/GetInfisicalEnvironmentCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/GetInfisicalEnvironmentCmdlet.cs new file mode 100644 index 0000000..e7f3d59 --- /dev/null +++ b/src/PSInfisicalAPI/Cmdlets/GetInfisicalEnvironmentCmdlet.cs @@ -0,0 +1,37 @@ +using System; +using System.Management.Automation; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Environments; +using PSInfisicalAPI.Models; + +namespace PSInfisicalAPI.Cmdlets +{ + [Cmdlet(VerbsCommon.Get, "InfisicalEnvironment")] + [OutputType(typeof(InfisicalEnvironment))] + public sealed class GetInfisicalEnvironmentCmdlet : InfisicalCmdletBase + { + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)] + [Alias("Slug", "Id", "Environment")] + public string EnvironmentSlugOrId { get; set; } + + [Parameter] public string ProjectId { get; set; } + + protected override void ProcessRecord() + { + try + { + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + InfisicalEnvironmentClient client = new InfisicalEnvironmentClient(HttpClient, Logger); + InfisicalEnvironment env = client.Retrieve(connection, ProjectId, EnvironmentSlugOrId); + if (env != null) + { + WriteObject(env); + } + } + catch (Exception exception) + { + ThrowTerminatingForException("GetInfisicalEnvironmentCmdlet", "RetrieveEnvironment", exception); + } + } + } +} diff --git a/src/PSInfisicalAPI/Cmdlets/GetInfisicalEnvironmentsCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/GetInfisicalEnvironmentsCmdlet.cs new file mode 100644 index 0000000..23879d9 --- /dev/null +++ b/src/PSInfisicalAPI/Cmdlets/GetInfisicalEnvironmentsCmdlet.cs @@ -0,0 +1,33 @@ +using System; +using System.Management.Automation; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Environments; +using PSInfisicalAPI.Models; + +namespace PSInfisicalAPI.Cmdlets +{ + [Cmdlet(VerbsCommon.Get, "InfisicalEnvironments")] + [OutputType(typeof(InfisicalEnvironment))] + public sealed class GetInfisicalEnvironmentsCmdlet : InfisicalCmdletBase + { + [Parameter] public string ProjectId { get; set; } + + protected override void ProcessRecord() + { + try + { + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + InfisicalEnvironmentClient client = new InfisicalEnvironmentClient(HttpClient, Logger); + InfisicalEnvironment[] envs = client.List(connection, ProjectId); + foreach (InfisicalEnvironment env in envs) + { + WriteObject(env); + } + } + catch (Exception exception) + { + ThrowTerminatingForException("GetInfisicalEnvironmentsCmdlet", "ListEnvironments", exception); + } + } + } +} diff --git a/src/PSInfisicalAPI/Cmdlets/NewInfisicalEnvironmentCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/NewInfisicalEnvironmentCmdlet.cs new file mode 100644 index 0000000..bcda139 --- /dev/null +++ b/src/PSInfisicalAPI/Cmdlets/NewInfisicalEnvironmentCmdlet.cs @@ -0,0 +1,41 @@ +using System; +using System.Management.Automation; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Environments; +using PSInfisicalAPI.Models; + +namespace PSInfisicalAPI.Cmdlets +{ + [Cmdlet(VerbsCommon.New, "InfisicalEnvironment", SupportsShouldProcess = true)] + [OutputType(typeof(InfisicalEnvironment))] + public sealed class NewInfisicalEnvironmentCmdlet : InfisicalCmdletBase + { + [Parameter(Mandatory = true, Position = 0)] public string Name { get; set; } + [Parameter(Mandatory = true, Position = 1)] public string Slug { get; set; } + [Parameter] public string ProjectId { get; set; } + [Parameter] public int? Position { get; set; } + + protected override void ProcessRecord() + { + try + { + if (!ShouldProcess(Slug, "Create Infisical environment")) + { + return; + } + + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + InfisicalEnvironmentClient client = new InfisicalEnvironmentClient(HttpClient, Logger); + InfisicalEnvironment env = client.Create(connection, ProjectId, Name, Slug, Position); + if (env != null) + { + WriteObject(env); + } + } + catch (Exception exception) + { + ThrowTerminatingForException("NewInfisicalEnvironmentCmdlet", "CreateEnvironment", exception); + } + } + } +} diff --git a/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalEnvironmentCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalEnvironmentCmdlet.cs new file mode 100644 index 0000000..54bd9ff --- /dev/null +++ b/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalEnvironmentCmdlet.cs @@ -0,0 +1,42 @@ +using System; +using System.Management.Automation; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Environments; + +namespace PSInfisicalAPI.Cmdlets +{ + [Cmdlet(VerbsCommon.Remove, "InfisicalEnvironment", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)] + public sealed class RemoveInfisicalEnvironmentCmdlet : InfisicalCmdletBase + { + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)] + [Alias("Id")] + public string EnvironmentId { get; set; } + + [Parameter] public string ProjectId { get; set; } + [Parameter] public SwitchParameter PassThru { get; set; } + + protected override void ProcessRecord() + { + try + { + if (!ShouldProcess(EnvironmentId, "Remove Infisical environment")) + { + return; + } + + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + InfisicalEnvironmentClient client = new InfisicalEnvironmentClient(HttpClient, Logger); + client.Delete(connection, ProjectId, EnvironmentId); + + if (PassThru.IsPresent) + { + WriteObject(EnvironmentId); + } + } + catch (Exception exception) + { + ThrowTerminatingForException("RemoveInfisicalEnvironmentCmdlet", "DeleteEnvironment", exception); + } + } + } +} diff --git a/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalEnvironmentCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalEnvironmentCmdlet.cs new file mode 100644 index 0000000..ade169e --- /dev/null +++ b/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalEnvironmentCmdlet.cs @@ -0,0 +1,45 @@ +using System; +using System.Management.Automation; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Environments; +using PSInfisicalAPI.Models; + +namespace PSInfisicalAPI.Cmdlets +{ + [Cmdlet(VerbsData.Update, "InfisicalEnvironment", SupportsShouldProcess = true)] + [OutputType(typeof(InfisicalEnvironment))] + public sealed class UpdateInfisicalEnvironmentCmdlet : InfisicalCmdletBase + { + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)] + [Alias("Id")] + public string EnvironmentId { get; set; } + + [Parameter] public string ProjectId { get; set; } + [Parameter] public string Name { get; set; } + [Parameter] public string Slug { get; set; } + [Parameter] public int? Position { get; set; } + + protected override void ProcessRecord() + { + try + { + if (!ShouldProcess(EnvironmentId, "Update Infisical environment")) + { + return; + } + + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + InfisicalEnvironmentClient client = new InfisicalEnvironmentClient(HttpClient, Logger); + InfisicalEnvironment env = client.Update(connection, ProjectId, EnvironmentId, Name, Slug, Position); + if (env != null) + { + WriteObject(env); + } + } + catch (Exception exception) + { + ThrowTerminatingForException("UpdateInfisicalEnvironmentCmdlet", "UpdateEnvironment", exception); + } + } + } +} diff --git a/src/PSInfisicalAPI/Environments/InfisicalEnvironmentClient.cs b/src/PSInfisicalAPI/Environments/InfisicalEnvironmentClient.cs new file mode 100644 index 0000000..3a917bc --- /dev/null +++ b/src/PSInfisicalAPI/Environments/InfisicalEnvironmentClient.cs @@ -0,0 +1,166 @@ +using System; +using System.Collections.Generic; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Endpoints; +using PSInfisicalAPI.Errors; +using PSInfisicalAPI.Http; +using PSInfisicalAPI.Logging; +using PSInfisicalAPI.Models; +using PSInfisicalAPI.Serialization; + +namespace PSInfisicalAPI.Environments +{ + public sealed class InfisicalEnvironmentClient + { + private const string Component = "EnvironmentClient"; + + private readonly IInfisicalLogger _logger; + private readonly JsonInfisicalSerializer _serializer; + private readonly InfisicalApiInvoker _invoker; + + public InfisicalEnvironmentClient(IInfisicalHttpClient httpClient, IInfisicalLogger logger) + { + if (httpClient == null) { throw new ArgumentNullException(nameof(httpClient)); } + _logger = logger ?? NullInfisicalLogger.Instance; + _serializer = new JsonInfisicalSerializer(); + _invoker = new InfisicalApiInvoker(httpClient); + } + + public InfisicalEnvironment[] List(InfisicalConnection connection, string projectId) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + string resolvedProjectId = FirstNonEmpty(projectId, connection.ProjectId); + if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); } + + Dictionary pathParameters = new Dictionary { { "projectId", resolvedProjectId } }; + + try + { + _logger.Information(Component, "Attempting to list Infisical environments. Please Wait..."); + InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.ListEnvironments, "ListEnvironments", pathParameters, null, null); + InfisicalEnvironmentWorkspaceWrapperDto dto = _serializer.Deserialize(response.Body); + response.Clear(); + + InfisicalEnvironmentWorkspaceDto workspace = dto != null ? (dto.Workspace ?? dto.Project) : null; + List envs = workspace != null ? workspace.Environments : null; + InfisicalEnvironment[] mapped = InfisicalEnvironmentMapper.MapMany(envs, resolvedProjectId); + _logger.Information(Component, "Infisical environment list retrieval was successful."); + return mapped; + } + catch (Exception) + { + _logger.Error(Component, "Infisical environment list retrieval failed."); + throw; + } + } + + public InfisicalEnvironment Retrieve(InfisicalConnection connection, string projectId, string environmentSlugOrId) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + string resolvedProjectId = FirstNonEmpty(projectId, connection.ProjectId); + if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); } + if (string.IsNullOrEmpty(environmentSlugOrId)) { throw new InfisicalConfigurationException("Environment is required."); } + + InfisicalEnvironment[] all = List(connection, resolvedProjectId); + foreach (InfisicalEnvironment env in all) + { + if (string.Equals(env.Id, environmentSlugOrId, StringComparison.OrdinalIgnoreCase) || + string.Equals(env.Slug, environmentSlugOrId, StringComparison.OrdinalIgnoreCase)) + { + return env; + } + } + + return null; + } + + public InfisicalEnvironment Create(InfisicalConnection connection, string projectId, string name, string slug, int? position) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + string resolvedProjectId = FirstNonEmpty(projectId, connection.ProjectId); + if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); } + if (string.IsNullOrEmpty(name)) { throw new InfisicalConfigurationException("Name is required."); } + if (string.IsNullOrEmpty(slug)) { throw new InfisicalConfigurationException("Slug is required."); } + + Dictionary pathParameters = new Dictionary { { "projectId", resolvedProjectId } }; + InfisicalEnvironmentCreateRequestDto request = new InfisicalEnvironmentCreateRequestDto { Name = name, Slug = slug, Position = position }; + string body = _serializer.Serialize(request); + + try + { + _logger.Information(Component, string.Concat("Attempting to create Infisical environment '", slug, "'. Please Wait...")); + InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.CreateEnvironment, "CreateEnvironment", pathParameters, null, body); + InfisicalEnvironmentSingleResponseDto dto = _serializer.Deserialize(response.Body); + response.Clear(); + + InfisicalEnvironment mapped = InfisicalEnvironmentMapper.Map(dto != null ? dto.Environment : null, resolvedProjectId); + _logger.Information(Component, "Infisical environment creation was successful."); + return mapped; + } + catch (Exception) + { + _logger.Error(Component, "Infisical environment creation failed."); + throw; + } + } + + public InfisicalEnvironment Update(InfisicalConnection connection, string projectId, string environmentId, string name, string slug, int? position) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + string resolvedProjectId = FirstNonEmpty(projectId, connection.ProjectId); + if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); } + if (string.IsNullOrEmpty(environmentId)) { throw new InfisicalConfigurationException("EnvironmentId is required."); } + + Dictionary pathParameters = new Dictionary { { "projectId", resolvedProjectId }, { "environmentId", environmentId } }; + InfisicalEnvironmentUpdateRequestDto request = new InfisicalEnvironmentUpdateRequestDto { Name = name, Slug = slug, Position = position }; + string body = _serializer.Serialize(request); + + try + { + _logger.Information(Component, string.Concat("Attempting to update Infisical environment '", environmentId, "'. Please Wait...")); + InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.UpdateEnvironment, "UpdateEnvironment", pathParameters, null, body); + InfisicalEnvironmentSingleResponseDto dto = _serializer.Deserialize(response.Body); + response.Clear(); + + InfisicalEnvironment mapped = InfisicalEnvironmentMapper.Map(dto != null ? dto.Environment : null, resolvedProjectId); + _logger.Information(Component, "Infisical environment update was successful."); + return mapped; + } + catch (Exception) + { + _logger.Error(Component, "Infisical environment update failed."); + throw; + } + } + + public void Delete(InfisicalConnection connection, string projectId, string environmentId) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + string resolvedProjectId = FirstNonEmpty(projectId, connection.ProjectId); + if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); } + if (string.IsNullOrEmpty(environmentId)) { throw new InfisicalConfigurationException("EnvironmentId is required."); } + + Dictionary pathParameters = new Dictionary { { "projectId", resolvedProjectId }, { "environmentId", environmentId } }; + + try + { + _logger.Information(Component, string.Concat("Attempting to delete Infisical environment '", environmentId, "'. Please Wait...")); + InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.DeleteEnvironment, "DeleteEnvironment", pathParameters, null, null); + response.Clear(); + _logger.Information(Component, "Infisical environment deletion was successful."); + } + catch (Exception) + { + _logger.Error(Component, "Infisical environment deletion failed."); + throw; + } + } + + private static string FirstNonEmpty(params string[] values) + { + if (values == null) { return null; } + foreach (string value in values) { if (!string.IsNullOrEmpty(value)) { return value; } } + return null; + } + } +} diff --git a/src/PSInfisicalAPI/Environments/InfisicalEnvironmentDtos.cs b/src/PSInfisicalAPI/Environments/InfisicalEnvironmentDtos.cs new file mode 100644 index 0000000..de52268 --- /dev/null +++ b/src/PSInfisicalAPI/Environments/InfisicalEnvironmentDtos.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace PSInfisicalAPI.Environments +{ + internal sealed class InfisicalEnvironmentResponseDto + { + [JsonProperty("id")] public string Id { get; set; } + [JsonProperty("_id")] public string InternalId { get; set; } + [JsonProperty("name")] public string Name { get; set; } + [JsonProperty("slug")] public string Slug { get; set; } + [JsonProperty("position")] public int? Position { get; set; } + [JsonProperty("projectId")] public string ProjectId { get; set; } + [JsonProperty("workspaceId")] public string WorkspaceId { get; set; } + } + + internal sealed class InfisicalEnvironmentSingleResponseDto + { + [JsonProperty("environment")] public InfisicalEnvironmentResponseDto Environment { get; set; } + } + + internal sealed class InfisicalEnvironmentWorkspaceWrapperDto + { + [JsonProperty("workspace")] public InfisicalEnvironmentWorkspaceDto Workspace { get; set; } + [JsonProperty("project")] public InfisicalEnvironmentWorkspaceDto Project { get; set; } + } + + internal sealed class InfisicalEnvironmentWorkspaceDto + { + [JsonProperty("id")] public string Id { get; set; } + [JsonProperty("environments")] public List Environments { get; set; } + } + + internal sealed class InfisicalEnvironmentCreateRequestDto + { + [JsonProperty("name")] public string Name { get; set; } + [JsonProperty("slug")] public string Slug { get; set; } + [JsonProperty("position", NullValueHandling = NullValueHandling.Ignore)] public int? Position { get; set; } + } + + internal sealed class InfisicalEnvironmentUpdateRequestDto + { + [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)] public string Name { get; set; } + [JsonProperty("slug", NullValueHandling = NullValueHandling.Ignore)] public string Slug { get; set; } + [JsonProperty("position", NullValueHandling = NullValueHandling.Ignore)] public int? Position { get; set; } + } +} diff --git a/src/PSInfisicalAPI/Environments/InfisicalEnvironmentMapper.cs b/src/PSInfisicalAPI/Environments/InfisicalEnvironmentMapper.cs new file mode 100644 index 0000000..03e9200 --- /dev/null +++ b/src/PSInfisicalAPI/Environments/InfisicalEnvironmentMapper.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using PSInfisicalAPI.Models; + +namespace PSInfisicalAPI.Environments +{ + internal static class InfisicalEnvironmentMapper + { + public static InfisicalEnvironment Map(InfisicalEnvironmentResponseDto dto, string fallbackProjectId) + { + if (dto == null) + { + return null; + } + + string projectId = !string.IsNullOrEmpty(dto.ProjectId) + ? dto.ProjectId + : (!string.IsNullOrEmpty(dto.WorkspaceId) ? dto.WorkspaceId : fallbackProjectId); + + return new InfisicalEnvironment + { + Id = !string.IsNullOrEmpty(dto.Id) ? dto.Id : dto.InternalId, + Name = dto.Name, + Slug = dto.Slug, + Position = dto.Position, + ProjectId = projectId + }; + } + + public static InfisicalEnvironment[] MapMany(IEnumerable items, string fallbackProjectId) + { + if (items == null) + { + return Array.Empty(); + } + + List results = new List(); + foreach (InfisicalEnvironmentResponseDto dto in items) + { + InfisicalEnvironment mapped = Map(dto, fallbackProjectId); + if (mapped != null) + { + results.Add(mapped); + } + } + + return results.ToArray(); + } + } +} diff --git a/src/PSInfisicalAPI/Models/InfisicalEnvironment.cs b/src/PSInfisicalAPI/Models/InfisicalEnvironment.cs new file mode 100644 index 0000000..d92f035 --- /dev/null +++ b/src/PSInfisicalAPI/Models/InfisicalEnvironment.cs @@ -0,0 +1,16 @@ +namespace PSInfisicalAPI.Models +{ + public sealed class InfisicalEnvironment + { + public string Id { get; set; } + public string Name { get; set; } + public string Slug { get; set; } + public int? Position { get; set; } + public string ProjectId { get; set; } + + public override string ToString() + { + return string.IsNullOrEmpty(Slug) ? (Name ?? Id) : Slug; + } + } +} -- 2.52.0 From 53161449333a7769c4acddf2ac24c8359bdc47ea Mon Sep 17 00:00:00 2001 From: GraceSolutions Date: Wed, 3 Jun 2026 17:25:46 -0400 Subject: [PATCH 05/12] M4: Folders CRUD - model, DTOs, mapper, client, 5 cmdlets + tests --- build.ps1 | 9 +- src/PSInfisicalAPI.Tests/FolderMapperTests.cs | 80 +++++++ .../Cmdlets/GetInfisicalFolderCmdlet.cs | 39 ++++ .../Cmdlets/GetInfisicalFoldersCmdlet.cs | 35 ++++ .../Cmdlets/NewInfisicalFolderCmdlet.cs | 41 ++++ .../Cmdlets/RemoveInfisicalFolderCmdlet.cs | 44 ++++ .../Cmdlets/UpdateInfisicalFolderCmdlet.cs | 45 ++++ .../Folders/InfisicalFolderClient.cs | 195 ++++++++++++++++++ .../Folders/InfisicalFolderDtos.cs | 47 +++++ .../Folders/InfisicalFolderMapper.cs | 72 +++++++ src/PSInfisicalAPI/Models/InfisicalFolder.cs | 21 ++ 11 files changed, 626 insertions(+), 2 deletions(-) create mode 100644 src/PSInfisicalAPI.Tests/FolderMapperTests.cs create mode 100644 src/PSInfisicalAPI/Cmdlets/GetInfisicalFolderCmdlet.cs create mode 100644 src/PSInfisicalAPI/Cmdlets/GetInfisicalFoldersCmdlet.cs create mode 100644 src/PSInfisicalAPI/Cmdlets/NewInfisicalFolderCmdlet.cs create mode 100644 src/PSInfisicalAPI/Cmdlets/RemoveInfisicalFolderCmdlet.cs create mode 100644 src/PSInfisicalAPI/Cmdlets/UpdateInfisicalFolderCmdlet.cs create mode 100644 src/PSInfisicalAPI/Folders/InfisicalFolderClient.cs create mode 100644 src/PSInfisicalAPI/Folders/InfisicalFolderDtos.cs create mode 100644 src/PSInfisicalAPI/Folders/InfisicalFolderMapper.cs create mode 100644 src/PSInfisicalAPI/Models/InfisicalFolder.cs diff --git a/build.ps1 b/build.ps1 index 7ba58a5..78bdbdd 100644 --- a/build.ps1 +++ b/build.ps1 @@ -113,7 +113,12 @@ function Write-Manifest { 'Get-InfisicalEnvironment', 'New-InfisicalEnvironment', 'Update-InfisicalEnvironment', - 'Remove-InfisicalEnvironment' + 'Remove-InfisicalEnvironment', + 'Get-InfisicalFolders', + 'Get-InfisicalFolder', + 'New-InfisicalFolder', + 'Update-InfisicalFolder', + 'Remove-InfisicalFolder' ) AliasesToExport = @() VariablesToExport = @() @@ -173,7 +178,7 @@ if (`$null -eq `$manifest) { Import-Module -Name '$($ModuleDirectory.FullName)' -Force -`$cmds = @('Connect-Infisical','Disconnect-Infisical','Get-InfisicalSecrets','Get-InfisicalSecret','ConvertTo-InfisicalSecretDictionary','Export-InfisicalSecrets','Get-InfisicalProjects','Get-InfisicalProject','New-InfisicalProject','Update-InfisicalProject','Remove-InfisicalProject','Get-InfisicalEnvironments','Get-InfisicalEnvironment','New-InfisicalEnvironment','Update-InfisicalEnvironment','Remove-InfisicalEnvironment') +`$cmds = @('Connect-Infisical','Disconnect-Infisical','Get-InfisicalSecrets','Get-InfisicalSecret','ConvertTo-InfisicalSecretDictionary','Export-InfisicalSecrets','Get-InfisicalProjects','Get-InfisicalProject','New-InfisicalProject','Update-InfisicalProject','Remove-InfisicalProject','Get-InfisicalEnvironments','Get-InfisicalEnvironment','New-InfisicalEnvironment','Update-InfisicalEnvironment','Remove-InfisicalEnvironment','Get-InfisicalFolders','Get-InfisicalFolder','New-InfisicalFolder','Update-InfisicalFolder','Remove-InfisicalFolder') foreach (`$c in `$cmds) { if (-not (Get-Command -Name `$c -Module PSInfisicalAPI -ErrorAction SilentlyContinue)) { throw "Cmdlet not found: `$c" diff --git a/src/PSInfisicalAPI.Tests/FolderMapperTests.cs b/src/PSInfisicalAPI.Tests/FolderMapperTests.cs new file mode 100644 index 0000000..f4427a7 --- /dev/null +++ b/src/PSInfisicalAPI.Tests/FolderMapperTests.cs @@ -0,0 +1,80 @@ +using System.Reflection; +using PSInfisicalAPI.Models; +using Xunit; + +namespace PSInfisicalAPI.Tests +{ + public class FolderMapperTests + { + private static readonly System.Type MapperType = typeof(PSInfisicalAPI.Connections.InfisicalConnection).Assembly + .GetType("PSInfisicalAPI.Folders.InfisicalFolderMapper", true); + + private static readonly System.Type DtoType = typeof(PSInfisicalAPI.Connections.InfisicalConnection).Assembly + .GetType("PSInfisicalAPI.Folders.InfisicalFolderResponseDto", true); + + private static InfisicalFolder InvokeMap(object dto, string fallbackProjectId, string fallbackEnvironment) + { + MethodInfo map = MapperType.GetMethod("Map", BindingFlags.Public | BindingFlags.Static); + return (InfisicalFolder)map.Invoke(null, new object[] { dto, fallbackProjectId, fallbackEnvironment }); + } + + [Fact] + public void Map_Null_Returns_Null() + { + Assert.Null(InvokeMap(null, "proj-x", "dev")); + } + + [Fact] + public void Map_Populates_Fields_With_Explicit_ProjectId() + { + object dto = System.Activator.CreateInstance(DtoType); + DtoType.GetProperty("Id").SetValue(dto, "fld-001"); + DtoType.GetProperty("Name").SetValue(dto, "config"); + DtoType.GetProperty("Path").SetValue(dto, "/app/config"); + DtoType.GetProperty("ParentId").SetValue(dto, "fld-root"); + DtoType.GetProperty("Environment").SetValue(dto, "prod"); + DtoType.GetProperty("ProjectId").SetValue(dto, "proj-001"); + + InfisicalFolder folder = InvokeMap(dto, "fallback-proj", "fallback-env"); + + Assert.Equal("fld-001", folder.Id); + Assert.Equal("config", folder.Name); + Assert.Equal("/app/config", folder.Path); + Assert.Equal("fld-root", folder.ParentId); + Assert.Equal("prod", folder.Environment); + Assert.Equal("proj-001", folder.ProjectId); + } + + [Fact] + public void Map_Uses_WorkspaceId_When_ProjectId_Empty() + { + object dto = System.Activator.CreateInstance(DtoType); + DtoType.GetProperty("Id").SetValue(dto, "fld-002"); + DtoType.GetProperty("WorkspaceId").SetValue(dto, "wks-002"); + + InfisicalFolder folder = InvokeMap(dto, "fallback-proj", "fallback-env"); + Assert.Equal("wks-002", folder.ProjectId); + } + + [Fact] + public void Map_Uses_Fallback_When_No_ProjectId_Or_Environment() + { + object dto = System.Activator.CreateInstance(DtoType); + DtoType.GetProperty("Id").SetValue(dto, "fld-003"); + + InfisicalFolder folder = InvokeMap(dto, "fallback-proj", "fallback-env"); + Assert.Equal("fallback-proj", folder.ProjectId); + Assert.Equal("fallback-env", folder.Environment); + } + + [Fact] + public void Map_Falls_Back_To_InternalId_For_Id() + { + object dto = System.Activator.CreateInstance(DtoType); + DtoType.GetProperty("InternalId").SetValue(dto, "internal-fld"); + + InfisicalFolder folder = InvokeMap(dto, "p", "e"); + Assert.Equal("internal-fld", folder.Id); + } + } +} diff --git a/src/PSInfisicalAPI/Cmdlets/GetInfisicalFolderCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/GetInfisicalFolderCmdlet.cs new file mode 100644 index 0000000..30da2af --- /dev/null +++ b/src/PSInfisicalAPI/Cmdlets/GetInfisicalFolderCmdlet.cs @@ -0,0 +1,39 @@ +using System; +using System.Management.Automation; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Folders; +using PSInfisicalAPI.Models; + +namespace PSInfisicalAPI.Cmdlets +{ + [Cmdlet(VerbsCommon.Get, "InfisicalFolder")] + [OutputType(typeof(InfisicalFolder))] + public sealed class GetInfisicalFolderCmdlet : InfisicalCmdletBase + { + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)] + [Alias("Name", "Id")] + public string FolderNameOrId { get; set; } + + [Parameter] public string ProjectId { get; set; } + [Parameter] public string Environment { get; set; } + [Parameter] public string Path { get; set; } + + protected override void ProcessRecord() + { + try + { + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + InfisicalFolderClient client = new InfisicalFolderClient(HttpClient, Logger); + InfisicalFolder folder = client.Retrieve(connection, ProjectId, Environment, Path, FolderNameOrId); + if (folder != null) + { + WriteObject(folder); + } + } + catch (Exception exception) + { + ThrowTerminatingForException("GetInfisicalFolderCmdlet", "RetrieveFolder", exception); + } + } + } +} diff --git a/src/PSInfisicalAPI/Cmdlets/GetInfisicalFoldersCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/GetInfisicalFoldersCmdlet.cs new file mode 100644 index 0000000..7c64a2b --- /dev/null +++ b/src/PSInfisicalAPI/Cmdlets/GetInfisicalFoldersCmdlet.cs @@ -0,0 +1,35 @@ +using System; +using System.Management.Automation; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Folders; +using PSInfisicalAPI.Models; + +namespace PSInfisicalAPI.Cmdlets +{ + [Cmdlet(VerbsCommon.Get, "InfisicalFolders")] + [OutputType(typeof(InfisicalFolder))] + public sealed class GetInfisicalFoldersCmdlet : InfisicalCmdletBase + { + [Parameter] public string ProjectId { get; set; } + [Parameter] public string Environment { get; set; } + [Parameter] public string Path { get; set; } + + protected override void ProcessRecord() + { + try + { + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + InfisicalFolderClient client = new InfisicalFolderClient(HttpClient, Logger); + InfisicalFolder[] folders = client.List(connection, ProjectId, Environment, Path); + foreach (InfisicalFolder folder in folders) + { + WriteObject(folder); + } + } + catch (Exception exception) + { + ThrowTerminatingForException("GetInfisicalFoldersCmdlet", "ListFolders", exception); + } + } + } +} diff --git a/src/PSInfisicalAPI/Cmdlets/NewInfisicalFolderCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/NewInfisicalFolderCmdlet.cs new file mode 100644 index 0000000..ac1d044 --- /dev/null +++ b/src/PSInfisicalAPI/Cmdlets/NewInfisicalFolderCmdlet.cs @@ -0,0 +1,41 @@ +using System; +using System.Management.Automation; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Folders; +using PSInfisicalAPI.Models; + +namespace PSInfisicalAPI.Cmdlets +{ + [Cmdlet(VerbsCommon.New, "InfisicalFolder", SupportsShouldProcess = true)] + [OutputType(typeof(InfisicalFolder))] + public sealed class NewInfisicalFolderCmdlet : InfisicalCmdletBase + { + [Parameter(Mandatory = true, Position = 0)] public string Name { get; set; } + [Parameter] public string ProjectId { get; set; } + [Parameter] public string Environment { get; set; } + [Parameter] public string Path { get; set; } + + protected override void ProcessRecord() + { + try + { + if (!ShouldProcess(Name, "Create Infisical folder")) + { + return; + } + + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + InfisicalFolderClient client = new InfisicalFolderClient(HttpClient, Logger); + InfisicalFolder folder = client.Create(connection, ProjectId, Environment, Name, Path); + if (folder != null) + { + WriteObject(folder); + } + } + catch (Exception exception) + { + ThrowTerminatingForException("NewInfisicalFolderCmdlet", "CreateFolder", exception); + } + } + } +} diff --git a/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalFolderCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalFolderCmdlet.cs new file mode 100644 index 0000000..825bdf3 --- /dev/null +++ b/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalFolderCmdlet.cs @@ -0,0 +1,44 @@ +using System; +using System.Management.Automation; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Folders; + +namespace PSInfisicalAPI.Cmdlets +{ + [Cmdlet(VerbsCommon.Remove, "InfisicalFolder", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)] + public sealed class RemoveInfisicalFolderCmdlet : InfisicalCmdletBase + { + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)] + [Alias("Id")] + public string FolderId { get; set; } + + [Parameter] public string ProjectId { get; set; } + [Parameter] public string Environment { get; set; } + [Parameter] public string Path { get; set; } + [Parameter] public SwitchParameter PassThru { get; set; } + + protected override void ProcessRecord() + { + try + { + if (!ShouldProcess(FolderId, "Remove Infisical folder")) + { + return; + } + + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + InfisicalFolderClient client = new InfisicalFolderClient(HttpClient, Logger); + client.Delete(connection, ProjectId, Environment, FolderId, Path); + + if (PassThru.IsPresent) + { + WriteObject(FolderId); + } + } + catch (Exception exception) + { + ThrowTerminatingForException("RemoveInfisicalFolderCmdlet", "DeleteFolder", exception); + } + } + } +} diff --git a/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalFolderCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalFolderCmdlet.cs new file mode 100644 index 0000000..891b398 --- /dev/null +++ b/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalFolderCmdlet.cs @@ -0,0 +1,45 @@ +using System; +using System.Management.Automation; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Folders; +using PSInfisicalAPI.Models; + +namespace PSInfisicalAPI.Cmdlets +{ + [Cmdlet(VerbsData.Update, "InfisicalFolder", SupportsShouldProcess = true)] + [OutputType(typeof(InfisicalFolder))] + public sealed class UpdateInfisicalFolderCmdlet : InfisicalCmdletBase + { + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)] + [Alias("Id")] + public string FolderId { get; set; } + + [Parameter(Mandatory = true, Position = 1)] public string Name { get; set; } + [Parameter] public string ProjectId { get; set; } + [Parameter] public string Environment { get; set; } + [Parameter] public string Path { get; set; } + + protected override void ProcessRecord() + { + try + { + if (!ShouldProcess(FolderId, "Update Infisical folder")) + { + return; + } + + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + InfisicalFolderClient client = new InfisicalFolderClient(HttpClient, Logger); + InfisicalFolder folder = client.Update(connection, ProjectId, Environment, FolderId, Name, Path); + if (folder != null) + { + WriteObject(folder); + } + } + catch (Exception exception) + { + ThrowTerminatingForException("UpdateInfisicalFolderCmdlet", "UpdateFolder", exception); + } + } + } +} diff --git a/src/PSInfisicalAPI/Folders/InfisicalFolderClient.cs b/src/PSInfisicalAPI/Folders/InfisicalFolderClient.cs new file mode 100644 index 0000000..4722add --- /dev/null +++ b/src/PSInfisicalAPI/Folders/InfisicalFolderClient.cs @@ -0,0 +1,195 @@ +using System; +using System.Collections.Generic; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Endpoints; +using PSInfisicalAPI.Errors; +using PSInfisicalAPI.Http; +using PSInfisicalAPI.Logging; +using PSInfisicalAPI.Models; +using PSInfisicalAPI.Serialization; + +namespace PSInfisicalAPI.Folders +{ + public sealed class InfisicalFolderClient + { + private const string Component = "FolderClient"; + + private readonly IInfisicalLogger _logger; + private readonly JsonInfisicalSerializer _serializer; + private readonly InfisicalApiInvoker _invoker; + + public InfisicalFolderClient(IInfisicalHttpClient httpClient, IInfisicalLogger logger) + { + if (httpClient == null) { throw new ArgumentNullException(nameof(httpClient)); } + _logger = logger ?? NullInfisicalLogger.Instance; + _serializer = new JsonInfisicalSerializer(); + _invoker = new InfisicalApiInvoker(httpClient); + } + + public InfisicalFolder[] List(InfisicalConnection connection, string projectId, string environment, string path) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + string resolvedProjectId = FirstNonEmpty(projectId, connection.ProjectId); + string resolvedEnvironment = FirstNonEmpty(environment, connection.Environment); + string resolvedPath = FirstNonEmpty(path, connection.DefaultSecretPath, "/"); + if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); } + if (string.IsNullOrEmpty(resolvedEnvironment)) { throw new InfisicalConfigurationException("Environment is required."); } + + List> queryParameters = new List> + { + new KeyValuePair("workspaceId", resolvedProjectId), + new KeyValuePair("environment", resolvedEnvironment), + new KeyValuePair("path", resolvedPath) + }; + + try + { + _logger.Information(Component, "Attempting to list Infisical folders. Please Wait..."); + InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.ListFolders, "ListFolders", null, queryParameters, null); + InfisicalFolderListResponseDto dto = _serializer.Deserialize(response.Body); + response.Clear(); + + InfisicalFolder[] mapped = InfisicalFolderMapper.MapMany(dto != null ? dto.Folders : null, resolvedProjectId, resolvedEnvironment); + _logger.Information(Component, "Infisical folder list retrieval was successful."); + return mapped; + } + catch (Exception) + { + _logger.Error(Component, "Infisical folder list retrieval failed."); + throw; + } + } + + public InfisicalFolder Retrieve(InfisicalConnection connection, string projectId, string environment, string path, string folderNameOrId) + { + if (string.IsNullOrEmpty(folderNameOrId)) { throw new InfisicalConfigurationException("Folder name or id is required."); } + + InfisicalFolder[] all = List(connection, projectId, environment, path); + foreach (InfisicalFolder folder in all) + { + if (string.Equals(folder.Id, folderNameOrId, StringComparison.OrdinalIgnoreCase) || + string.Equals(folder.Name, folderNameOrId, StringComparison.OrdinalIgnoreCase)) + { + return folder; + } + } + + return null; + } + + public InfisicalFolder Create(InfisicalConnection connection, string projectId, string environment, string name, string path) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + string resolvedProjectId = FirstNonEmpty(projectId, connection.ProjectId); + string resolvedEnvironment = FirstNonEmpty(environment, connection.Environment); + string resolvedPath = FirstNonEmpty(path, connection.DefaultSecretPath, "/"); + if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); } + if (string.IsNullOrEmpty(resolvedEnvironment)) { throw new InfisicalConfigurationException("Environment is required."); } + if (string.IsNullOrEmpty(name)) { throw new InfisicalConfigurationException("Name is required."); } + + InfisicalFolderCreateRequestDto request = new InfisicalFolderCreateRequestDto + { + WorkspaceId = resolvedProjectId, + Environment = resolvedEnvironment, + Name = name, + Path = resolvedPath + }; + string body = _serializer.Serialize(request); + + try + { + _logger.Information(Component, string.Concat("Attempting to create Infisical folder '", name, "'. Please Wait...")); + InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.CreateFolder, "CreateFolder", null, null, body); + InfisicalFolderSingleResponseDto dto = _serializer.Deserialize(response.Body); + response.Clear(); + + InfisicalFolder mapped = InfisicalFolderMapper.Map(dto != null ? dto.Folder : null, resolvedProjectId, resolvedEnvironment); + _logger.Information(Component, "Infisical folder creation was successful."); + return mapped; + } + catch (Exception) + { + _logger.Error(Component, "Infisical folder creation failed."); + throw; + } + } + + public InfisicalFolder Update(InfisicalConnection connection, string projectId, string environment, string folderId, string name, string path) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + string resolvedProjectId = FirstNonEmpty(projectId, connection.ProjectId); + string resolvedEnvironment = FirstNonEmpty(environment, connection.Environment); + string resolvedPath = FirstNonEmpty(path, connection.DefaultSecretPath, "/"); + if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); } + if (string.IsNullOrEmpty(resolvedEnvironment)) { throw new InfisicalConfigurationException("Environment is required."); } + if (string.IsNullOrEmpty(folderId)) { throw new InfisicalConfigurationException("FolderId is required."); } + if (string.IsNullOrEmpty(name)) { throw new InfisicalConfigurationException("Name is required."); } + + Dictionary pathParameters = new Dictionary { { "folderId", folderId } }; + InfisicalFolderUpdateRequestDto request = new InfisicalFolderUpdateRequestDto + { + WorkspaceId = resolvedProjectId, + Environment = resolvedEnvironment, + Name = name, + Path = resolvedPath + }; + string body = _serializer.Serialize(request); + + try + { + _logger.Information(Component, string.Concat("Attempting to update Infisical folder '", folderId, "'. Please Wait...")); + InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.UpdateFolder, "UpdateFolder", pathParameters, null, body); + InfisicalFolderSingleResponseDto dto = _serializer.Deserialize(response.Body); + response.Clear(); + + InfisicalFolder mapped = InfisicalFolderMapper.Map(dto != null ? dto.Folder : null, resolvedProjectId, resolvedEnvironment); + _logger.Information(Component, "Infisical folder update was successful."); + return mapped; + } + catch (Exception) + { + _logger.Error(Component, "Infisical folder update failed."); + throw; + } + } + + public void Delete(InfisicalConnection connection, string projectId, string environment, string folderId, string path) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + string resolvedProjectId = FirstNonEmpty(projectId, connection.ProjectId); + string resolvedEnvironment = FirstNonEmpty(environment, connection.Environment); + string resolvedPath = FirstNonEmpty(path, connection.DefaultSecretPath, "/"); + if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); } + if (string.IsNullOrEmpty(resolvedEnvironment)) { throw new InfisicalConfigurationException("Environment is required."); } + if (string.IsNullOrEmpty(folderId)) { throw new InfisicalConfigurationException("FolderId is required."); } + + Dictionary pathParameters = new Dictionary { { "folderId", folderId } }; + List> queryParameters = new List> + { + new KeyValuePair("workspaceId", resolvedProjectId), + new KeyValuePair("environment", resolvedEnvironment), + new KeyValuePair("path", resolvedPath) + }; + + try + { + _logger.Information(Component, string.Concat("Attempting to delete Infisical folder '", folderId, "'. Please Wait...")); + InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.DeleteFolder, "DeleteFolder", pathParameters, queryParameters, null); + response.Clear(); + _logger.Information(Component, "Infisical folder deletion was successful."); + } + catch (Exception) + { + _logger.Error(Component, "Infisical folder deletion failed."); + throw; + } + } + + private static string FirstNonEmpty(params string[] values) + { + if (values == null) { return null; } + foreach (string value in values) { if (!string.IsNullOrEmpty(value)) { return value; } } + return null; + } + } +} diff --git a/src/PSInfisicalAPI/Folders/InfisicalFolderDtos.cs b/src/PSInfisicalAPI/Folders/InfisicalFolderDtos.cs new file mode 100644 index 0000000..5c29e2a --- /dev/null +++ b/src/PSInfisicalAPI/Folders/InfisicalFolderDtos.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace PSInfisicalAPI.Folders +{ + internal sealed class InfisicalFolderResponseDto + { + [JsonProperty("id")] public string Id { get; set; } + [JsonProperty("_id")] public string InternalId { get; set; } + [JsonProperty("name")] public string Name { get; set; } + [JsonProperty("path")] public string Path { get; set; } + [JsonProperty("parentId")] public string ParentId { get; set; } + [JsonProperty("envId")] public string EnvId { get; set; } + [JsonProperty("environment")] public string Environment { get; set; } + [JsonProperty("projectId")] public string ProjectId { get; set; } + [JsonProperty("workspaceId")] public string WorkspaceId { get; set; } + [JsonProperty("createdAt")] public string CreatedAt { get; set; } + [JsonProperty("updatedAt")] public string UpdatedAt { get; set; } + } + + internal sealed class InfisicalFolderListResponseDto + { + [JsonProperty("folders")] public List Folders { get; set; } + } + + internal sealed class InfisicalFolderSingleResponseDto + { + [JsonProperty("folder")] public InfisicalFolderResponseDto Folder { get; set; } + } + + internal sealed class InfisicalFolderCreateRequestDto + { + [JsonProperty("workspaceId")] public string WorkspaceId { get; set; } + [JsonProperty("environment")] public string Environment { get; set; } + [JsonProperty("name")] public string Name { get; set; } + [JsonProperty("path", NullValueHandling = NullValueHandling.Ignore)] public string Path { get; set; } + [JsonProperty("directory", NullValueHandling = NullValueHandling.Ignore)] public string Directory { get; set; } + } + + internal sealed class InfisicalFolderUpdateRequestDto + { + [JsonProperty("workspaceId")] public string WorkspaceId { get; set; } + [JsonProperty("environment")] public string Environment { get; set; } + [JsonProperty("name")] public string Name { get; set; } + [JsonProperty("path", NullValueHandling = NullValueHandling.Ignore)] public string Path { get; set; } + } +} diff --git a/src/PSInfisicalAPI/Folders/InfisicalFolderMapper.cs b/src/PSInfisicalAPI/Folders/InfisicalFolderMapper.cs new file mode 100644 index 0000000..9fa8b79 --- /dev/null +++ b/src/PSInfisicalAPI/Folders/InfisicalFolderMapper.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using PSInfisicalAPI.Models; + +namespace PSInfisicalAPI.Folders +{ + internal static class InfisicalFolderMapper + { + public static InfisicalFolder Map(InfisicalFolderResponseDto dto, string fallbackProjectId, string fallbackEnvironment) + { + if (dto == null) + { + return null; + } + + string projectId = !string.IsNullOrEmpty(dto.ProjectId) + ? dto.ProjectId + : (!string.IsNullOrEmpty(dto.WorkspaceId) ? dto.WorkspaceId : fallbackProjectId); + + string environment = !string.IsNullOrEmpty(dto.Environment) ? dto.Environment : fallbackEnvironment; + + return new InfisicalFolder + { + Id = !string.IsNullOrEmpty(dto.Id) ? dto.Id : dto.InternalId, + Name = dto.Name, + Path = dto.Path, + ParentId = dto.ParentId, + Environment = environment, + ProjectId = projectId, + CreatedAtUtc = ParseTimestamp(dto.CreatedAt), + UpdatedAtUtc = ParseTimestamp(dto.UpdatedAt) + }; + } + + public static InfisicalFolder[] MapMany(IEnumerable items, string fallbackProjectId, string fallbackEnvironment) + { + if (items == null) + { + return Array.Empty(); + } + + List results = new List(); + foreach (InfisicalFolderResponseDto dto in items) + { + InfisicalFolder mapped = Map(dto, fallbackProjectId, fallbackEnvironment); + if (mapped != null) + { + results.Add(mapped); + } + } + + return results.ToArray(); + } + + 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; + } + } +} diff --git a/src/PSInfisicalAPI/Models/InfisicalFolder.cs b/src/PSInfisicalAPI/Models/InfisicalFolder.cs new file mode 100644 index 0000000..17e4ca3 --- /dev/null +++ b/src/PSInfisicalAPI/Models/InfisicalFolder.cs @@ -0,0 +1,21 @@ +using System; + +namespace PSInfisicalAPI.Models +{ + public sealed class InfisicalFolder + { + public string Id { get; set; } + public string Name { get; set; } + public string Path { get; set; } + public string ParentId { get; set; } + public string Environment { get; set; } + public string ProjectId { get; set; } + public DateTimeOffset? CreatedAtUtc { get; set; } + public DateTimeOffset? UpdatedAtUtc { get; set; } + + public override string ToString() + { + return string.IsNullOrEmpty(Path) ? (Name ?? Id) : Path; + } + } +} -- 2.52.0 From 84ece43d296a0c6fecc8a2c0e28649242f43bf92 Mon Sep 17 00:00:00 2001 From: GraceSolutions Date: Wed, 3 Jun 2026 17:27:49 -0400 Subject: [PATCH 06/12] M5: Tags CRUD - model, DTOs, mapper, client, 5 cmdlets + tests --- build.ps1 | 9 +- src/PSInfisicalAPI.Tests/TagMapperTests.cs | 77 ++++++++ .../Cmdlets/GetInfisicalTagCmdlet.cs | 37 ++++ .../Cmdlets/GetInfisicalTagsCmdlet.cs | 33 ++++ .../Cmdlets/NewInfisicalTagCmdlet.cs | 41 +++++ .../Cmdlets/RemoveInfisicalTagCmdlet.cs | 42 +++++ .../Cmdlets/UpdateInfisicalTagCmdlet.cs | 45 +++++ src/PSInfisicalAPI/Models/InfisicalTag.cs | 20 +++ src/PSInfisicalAPI/Tags/InfisicalTagClient.cs | 166 ++++++++++++++++++ src/PSInfisicalAPI/Tags/InfisicalTagDtos.cs | 44 +++++ src/PSInfisicalAPI/Tags/InfisicalTagMapper.cs | 69 ++++++++ 11 files changed, 581 insertions(+), 2 deletions(-) create mode 100644 src/PSInfisicalAPI.Tests/TagMapperTests.cs create mode 100644 src/PSInfisicalAPI/Cmdlets/GetInfisicalTagCmdlet.cs create mode 100644 src/PSInfisicalAPI/Cmdlets/GetInfisicalTagsCmdlet.cs create mode 100644 src/PSInfisicalAPI/Cmdlets/NewInfisicalTagCmdlet.cs create mode 100644 src/PSInfisicalAPI/Cmdlets/RemoveInfisicalTagCmdlet.cs create mode 100644 src/PSInfisicalAPI/Cmdlets/UpdateInfisicalTagCmdlet.cs create mode 100644 src/PSInfisicalAPI/Models/InfisicalTag.cs create mode 100644 src/PSInfisicalAPI/Tags/InfisicalTagClient.cs create mode 100644 src/PSInfisicalAPI/Tags/InfisicalTagDtos.cs create mode 100644 src/PSInfisicalAPI/Tags/InfisicalTagMapper.cs diff --git a/build.ps1 b/build.ps1 index 78bdbdd..1591563 100644 --- a/build.ps1 +++ b/build.ps1 @@ -118,7 +118,12 @@ function Write-Manifest { 'Get-InfisicalFolder', 'New-InfisicalFolder', 'Update-InfisicalFolder', - 'Remove-InfisicalFolder' + 'Remove-InfisicalFolder', + 'Get-InfisicalTags', + 'Get-InfisicalTag', + 'New-InfisicalTag', + 'Update-InfisicalTag', + 'Remove-InfisicalTag' ) AliasesToExport = @() VariablesToExport = @() @@ -178,7 +183,7 @@ if (`$null -eq `$manifest) { Import-Module -Name '$($ModuleDirectory.FullName)' -Force -`$cmds = @('Connect-Infisical','Disconnect-Infisical','Get-InfisicalSecrets','Get-InfisicalSecret','ConvertTo-InfisicalSecretDictionary','Export-InfisicalSecrets','Get-InfisicalProjects','Get-InfisicalProject','New-InfisicalProject','Update-InfisicalProject','Remove-InfisicalProject','Get-InfisicalEnvironments','Get-InfisicalEnvironment','New-InfisicalEnvironment','Update-InfisicalEnvironment','Remove-InfisicalEnvironment','Get-InfisicalFolders','Get-InfisicalFolder','New-InfisicalFolder','Update-InfisicalFolder','Remove-InfisicalFolder') +`$cmds = @('Connect-Infisical','Disconnect-Infisical','Get-InfisicalSecrets','Get-InfisicalSecret','ConvertTo-InfisicalSecretDictionary','Export-InfisicalSecrets','Get-InfisicalProjects','Get-InfisicalProject','New-InfisicalProject','Update-InfisicalProject','Remove-InfisicalProject','Get-InfisicalEnvironments','Get-InfisicalEnvironment','New-InfisicalEnvironment','Update-InfisicalEnvironment','Remove-InfisicalEnvironment','Get-InfisicalFolders','Get-InfisicalFolder','New-InfisicalFolder','Update-InfisicalFolder','Remove-InfisicalFolder','Get-InfisicalTags','Get-InfisicalTag','New-InfisicalTag','Update-InfisicalTag','Remove-InfisicalTag') foreach (`$c in `$cmds) { if (-not (Get-Command -Name `$c -Module PSInfisicalAPI -ErrorAction SilentlyContinue)) { throw "Cmdlet not found: `$c" diff --git a/src/PSInfisicalAPI.Tests/TagMapperTests.cs b/src/PSInfisicalAPI.Tests/TagMapperTests.cs new file mode 100644 index 0000000..12af553 --- /dev/null +++ b/src/PSInfisicalAPI.Tests/TagMapperTests.cs @@ -0,0 +1,77 @@ +using System.Reflection; +using PSInfisicalAPI.Models; +using Xunit; + +namespace PSInfisicalAPI.Tests +{ + public class TagMapperTests + { + private static readonly System.Type MapperType = typeof(PSInfisicalAPI.Connections.InfisicalConnection).Assembly + .GetType("PSInfisicalAPI.Tags.InfisicalTagMapper", true); + + private static readonly System.Type DtoType = typeof(PSInfisicalAPI.Connections.InfisicalConnection).Assembly + .GetType("PSInfisicalAPI.Tags.InfisicalTagResponseDto", true); + + private static InfisicalTag InvokeMap(object dto, string fallbackProjectId) + { + MethodInfo map = MapperType.GetMethod("Map", BindingFlags.Public | BindingFlags.Static); + return (InfisicalTag)map.Invoke(null, new object[] { dto, fallbackProjectId }); + } + + [Fact] + public void Map_Null_Returns_Null() + { + Assert.Null(InvokeMap(null, "proj-x")); + } + + [Fact] + public void Map_Populates_Fields_With_Explicit_ProjectId() + { + object dto = System.Activator.CreateInstance(DtoType); + DtoType.GetProperty("Id").SetValue(dto, "tag-001"); + DtoType.GetProperty("Slug").SetValue(dto, "critical"); + DtoType.GetProperty("Name").SetValue(dto, "Critical"); + DtoType.GetProperty("Color").SetValue(dto, "#FF0000"); + DtoType.GetProperty("ProjectId").SetValue(dto, "proj-001"); + + InfisicalTag tag = InvokeMap(dto, "fallback-proj"); + + Assert.Equal("tag-001", tag.Id); + Assert.Equal("critical", tag.Slug); + Assert.Equal("Critical", tag.Name); + Assert.Equal("#FF0000", tag.Color); + Assert.Equal("proj-001", tag.ProjectId); + } + + [Fact] + public void Map_Uses_WorkspaceId_When_ProjectId_Empty() + { + object dto = System.Activator.CreateInstance(DtoType); + DtoType.GetProperty("Id").SetValue(dto, "tag-002"); + DtoType.GetProperty("WorkspaceId").SetValue(dto, "wks-002"); + + InfisicalTag tag = InvokeMap(dto, "fallback-proj"); + Assert.Equal("wks-002", tag.ProjectId); + } + + [Fact] + public void Map_Uses_Fallback_When_No_ProjectId_Or_WorkspaceId() + { + object dto = System.Activator.CreateInstance(DtoType); + DtoType.GetProperty("Id").SetValue(dto, "tag-003"); + + InfisicalTag tag = InvokeMap(dto, "fallback-proj"); + Assert.Equal("fallback-proj", tag.ProjectId); + } + + [Fact] + public void Map_Falls_Back_To_InternalId_For_Id() + { + object dto = System.Activator.CreateInstance(DtoType); + DtoType.GetProperty("InternalId").SetValue(dto, "internal-tag"); + + InfisicalTag tag = InvokeMap(dto, "p"); + Assert.Equal("internal-tag", tag.Id); + } + } +} diff --git a/src/PSInfisicalAPI/Cmdlets/GetInfisicalTagCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/GetInfisicalTagCmdlet.cs new file mode 100644 index 0000000..cc8d32e --- /dev/null +++ b/src/PSInfisicalAPI/Cmdlets/GetInfisicalTagCmdlet.cs @@ -0,0 +1,37 @@ +using System; +using System.Management.Automation; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Models; +using PSInfisicalAPI.Tags; + +namespace PSInfisicalAPI.Cmdlets +{ + [Cmdlet(VerbsCommon.Get, "InfisicalTag")] + [OutputType(typeof(InfisicalTag))] + public sealed class GetInfisicalTagCmdlet : InfisicalCmdletBase + { + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)] + [Alias("Slug", "Id")] + public string TagSlugOrId { get; set; } + + [Parameter] public string ProjectId { get; set; } + + protected override void ProcessRecord() + { + try + { + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + InfisicalTagClient client = new InfisicalTagClient(HttpClient, Logger); + InfisicalTag tag = client.Retrieve(connection, ProjectId, TagSlugOrId); + if (tag != null) + { + WriteObject(tag); + } + } + catch (Exception exception) + { + ThrowTerminatingForException("GetInfisicalTagCmdlet", "RetrieveTag", exception); + } + } + } +} diff --git a/src/PSInfisicalAPI/Cmdlets/GetInfisicalTagsCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/GetInfisicalTagsCmdlet.cs new file mode 100644 index 0000000..8b10649 --- /dev/null +++ b/src/PSInfisicalAPI/Cmdlets/GetInfisicalTagsCmdlet.cs @@ -0,0 +1,33 @@ +using System; +using System.Management.Automation; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Models; +using PSInfisicalAPI.Tags; + +namespace PSInfisicalAPI.Cmdlets +{ + [Cmdlet(VerbsCommon.Get, "InfisicalTags")] + [OutputType(typeof(InfisicalTag))] + public sealed class GetInfisicalTagsCmdlet : InfisicalCmdletBase + { + [Parameter] public string ProjectId { get; set; } + + protected override void ProcessRecord() + { + try + { + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + InfisicalTagClient client = new InfisicalTagClient(HttpClient, Logger); + InfisicalTag[] tags = client.List(connection, ProjectId); + foreach (InfisicalTag tag in tags) + { + WriteObject(tag); + } + } + catch (Exception exception) + { + ThrowTerminatingForException("GetInfisicalTagsCmdlet", "ListTags", exception); + } + } + } +} diff --git a/src/PSInfisicalAPI/Cmdlets/NewInfisicalTagCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/NewInfisicalTagCmdlet.cs new file mode 100644 index 0000000..21d99d0 --- /dev/null +++ b/src/PSInfisicalAPI/Cmdlets/NewInfisicalTagCmdlet.cs @@ -0,0 +1,41 @@ +using System; +using System.Management.Automation; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Models; +using PSInfisicalAPI.Tags; + +namespace PSInfisicalAPI.Cmdlets +{ + [Cmdlet(VerbsCommon.New, "InfisicalTag", SupportsShouldProcess = true)] + [OutputType(typeof(InfisicalTag))] + public sealed class NewInfisicalTagCmdlet : InfisicalCmdletBase + { + [Parameter(Mandatory = true, Position = 0)] public string Slug { get; set; } + [Parameter] public string Name { get; set; } + [Parameter] public string Color { get; set; } + [Parameter] public string ProjectId { get; set; } + + protected override void ProcessRecord() + { + try + { + if (!ShouldProcess(Slug, "Create Infisical tag")) + { + return; + } + + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + InfisicalTagClient client = new InfisicalTagClient(HttpClient, Logger); + InfisicalTag tag = client.Create(connection, ProjectId, Slug, Name, Color); + if (tag != null) + { + WriteObject(tag); + } + } + catch (Exception exception) + { + ThrowTerminatingForException("NewInfisicalTagCmdlet", "CreateTag", exception); + } + } + } +} diff --git a/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalTagCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalTagCmdlet.cs new file mode 100644 index 0000000..bb14432 --- /dev/null +++ b/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalTagCmdlet.cs @@ -0,0 +1,42 @@ +using System; +using System.Management.Automation; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Tags; + +namespace PSInfisicalAPI.Cmdlets +{ + [Cmdlet(VerbsCommon.Remove, "InfisicalTag", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)] + public sealed class RemoveInfisicalTagCmdlet : InfisicalCmdletBase + { + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)] + [Alias("Id")] + public string TagId { get; set; } + + [Parameter] public string ProjectId { get; set; } + [Parameter] public SwitchParameter PassThru { get; set; } + + protected override void ProcessRecord() + { + try + { + if (!ShouldProcess(TagId, "Remove Infisical tag")) + { + return; + } + + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + InfisicalTagClient client = new InfisicalTagClient(HttpClient, Logger); + client.Delete(connection, ProjectId, TagId); + + if (PassThru.IsPresent) + { + WriteObject(TagId); + } + } + catch (Exception exception) + { + ThrowTerminatingForException("RemoveInfisicalTagCmdlet", "DeleteTag", exception); + } + } + } +} diff --git a/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalTagCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalTagCmdlet.cs new file mode 100644 index 0000000..9a6c45b --- /dev/null +++ b/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalTagCmdlet.cs @@ -0,0 +1,45 @@ +using System; +using System.Management.Automation; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Models; +using PSInfisicalAPI.Tags; + +namespace PSInfisicalAPI.Cmdlets +{ + [Cmdlet(VerbsData.Update, "InfisicalTag", SupportsShouldProcess = true)] + [OutputType(typeof(InfisicalTag))] + public sealed class UpdateInfisicalTagCmdlet : InfisicalCmdletBase + { + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)] + [Alias("Id")] + public string TagId { get; set; } + + [Parameter] public string Slug { get; set; } + [Parameter] public string Name { get; set; } + [Parameter] public string Color { get; set; } + [Parameter] public string ProjectId { get; set; } + + protected override void ProcessRecord() + { + try + { + if (!ShouldProcess(TagId, "Update Infisical tag")) + { + return; + } + + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + InfisicalTagClient client = new InfisicalTagClient(HttpClient, Logger); + InfisicalTag tag = client.Update(connection, ProjectId, TagId, Slug, Name, Color); + if (tag != null) + { + WriteObject(tag); + } + } + catch (Exception exception) + { + ThrowTerminatingForException("UpdateInfisicalTagCmdlet", "UpdateTag", exception); + } + } + } +} diff --git a/src/PSInfisicalAPI/Models/InfisicalTag.cs b/src/PSInfisicalAPI/Models/InfisicalTag.cs new file mode 100644 index 0000000..4eec34c --- /dev/null +++ b/src/PSInfisicalAPI/Models/InfisicalTag.cs @@ -0,0 +1,20 @@ +using System; + +namespace PSInfisicalAPI.Models +{ + public sealed class InfisicalTag + { + public string Id { get; set; } + public string Slug { get; set; } + public string Name { get; set; } + public string Color { get; set; } + public string ProjectId { get; set; } + public DateTimeOffset? CreatedAtUtc { get; set; } + public DateTimeOffset? UpdatedAtUtc { get; set; } + + public override string ToString() + { + return Slug ?? Name ?? Id; + } + } +} diff --git a/src/PSInfisicalAPI/Tags/InfisicalTagClient.cs b/src/PSInfisicalAPI/Tags/InfisicalTagClient.cs new file mode 100644 index 0000000..9430ae2 --- /dev/null +++ b/src/PSInfisicalAPI/Tags/InfisicalTagClient.cs @@ -0,0 +1,166 @@ +using System; +using System.Collections.Generic; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Endpoints; +using PSInfisicalAPI.Errors; +using PSInfisicalAPI.Http; +using PSInfisicalAPI.Logging; +using PSInfisicalAPI.Models; +using PSInfisicalAPI.Serialization; + +namespace PSInfisicalAPI.Tags +{ + public sealed class InfisicalTagClient + { + private const string Component = "TagClient"; + + private readonly IInfisicalLogger _logger; + private readonly JsonInfisicalSerializer _serializer; + private readonly InfisicalApiInvoker _invoker; + + public InfisicalTagClient(IInfisicalHttpClient httpClient, IInfisicalLogger logger) + { + if (httpClient == null) { throw new ArgumentNullException(nameof(httpClient)); } + _logger = logger ?? NullInfisicalLogger.Instance; + _serializer = new JsonInfisicalSerializer(); + _invoker = new InfisicalApiInvoker(httpClient); + } + + public InfisicalTag[] List(InfisicalConnection connection, string projectId) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + string resolvedProjectId = FirstNonEmpty(projectId, connection.ProjectId); + if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); } + + Dictionary pathParameters = new Dictionary { { "projectId", resolvedProjectId } }; + + try + { + _logger.Information(Component, "Attempting to list Infisical tags. Please Wait..."); + InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.ListTags, "ListTags", pathParameters, null, null); + InfisicalTagListResponseDto dto = _serializer.Deserialize(response.Body); + response.Clear(); + + List source = dto != null ? (dto.WorkspaceTags ?? dto.Tags) : null; + InfisicalTag[] mapped = InfisicalTagMapper.MapMany(source, resolvedProjectId); + _logger.Information(Component, "Infisical tag list retrieval was successful."); + return mapped; + } + catch (Exception) + { + _logger.Error(Component, "Infisical tag list retrieval failed."); + throw; + } + } + + public InfisicalTag Retrieve(InfisicalConnection connection, string projectId, string tagSlugOrId) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + string resolvedProjectId = FirstNonEmpty(projectId, connection.ProjectId); + if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); } + if (string.IsNullOrEmpty(tagSlugOrId)) { throw new InfisicalConfigurationException("Tag slug or id is required."); } + + InfisicalTag[] all = List(connection, resolvedProjectId); + foreach (InfisicalTag tag in all) + { + if (string.Equals(tag.Id, tagSlugOrId, StringComparison.OrdinalIgnoreCase) || + string.Equals(tag.Slug, tagSlugOrId, StringComparison.OrdinalIgnoreCase)) + { + return tag; + } + } + + return null; + } + + public InfisicalTag Create(InfisicalConnection connection, string projectId, string slug, string name, string color) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + string resolvedProjectId = FirstNonEmpty(projectId, connection.ProjectId); + if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); } + if (string.IsNullOrEmpty(slug)) { throw new InfisicalConfigurationException("Slug is required."); } + + Dictionary pathParameters = new Dictionary { { "projectId", resolvedProjectId } }; + InfisicalTagCreateRequestDto request = new InfisicalTagCreateRequestDto { Slug = slug, Name = name, Color = color }; + string body = _serializer.Serialize(request); + + try + { + _logger.Information(Component, string.Concat("Attempting to create Infisical tag '", slug, "'. Please Wait...")); + InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.CreateTag, "CreateTag", pathParameters, null, body); + InfisicalTagSingleResponseDto dto = _serializer.Deserialize(response.Body); + response.Clear(); + + InfisicalTagResponseDto inner = dto != null ? (dto.WorkspaceTag ?? dto.Tag) : null; + InfisicalTag mapped = InfisicalTagMapper.Map(inner, resolvedProjectId); + _logger.Information(Component, "Infisical tag creation was successful."); + return mapped; + } + catch (Exception) + { + _logger.Error(Component, "Infisical tag creation failed."); + throw; + } + } + + public InfisicalTag Update(InfisicalConnection connection, string projectId, string tagId, string slug, string name, string color) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + string resolvedProjectId = FirstNonEmpty(projectId, connection.ProjectId); + if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); } + if (string.IsNullOrEmpty(tagId)) { throw new InfisicalConfigurationException("TagId is required."); } + + Dictionary pathParameters = new Dictionary { { "projectId", resolvedProjectId }, { "tagId", tagId } }; + InfisicalTagUpdateRequestDto request = new InfisicalTagUpdateRequestDto { Slug = slug, Name = name, Color = color }; + string body = _serializer.Serialize(request); + + try + { + _logger.Information(Component, string.Concat("Attempting to update Infisical tag '", tagId, "'. Please Wait...")); + InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.UpdateTag, "UpdateTag", pathParameters, null, body); + InfisicalTagSingleResponseDto dto = _serializer.Deserialize(response.Body); + response.Clear(); + + InfisicalTagResponseDto inner = dto != null ? (dto.WorkspaceTag ?? dto.Tag) : null; + InfisicalTag mapped = InfisicalTagMapper.Map(inner, resolvedProjectId); + _logger.Information(Component, "Infisical tag update was successful."); + return mapped; + } + catch (Exception) + { + _logger.Error(Component, "Infisical tag update failed."); + throw; + } + } + + public void Delete(InfisicalConnection connection, string projectId, string tagId) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + string resolvedProjectId = FirstNonEmpty(projectId, connection.ProjectId); + if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); } + if (string.IsNullOrEmpty(tagId)) { throw new InfisicalConfigurationException("TagId is required."); } + + Dictionary pathParameters = new Dictionary { { "projectId", resolvedProjectId }, { "tagId", tagId } }; + + try + { + _logger.Information(Component, string.Concat("Attempting to delete Infisical tag '", tagId, "'. Please Wait...")); + InfisicalHttpResponse response = _invoker.Invoke(connection, InfisicalEndpointNames.DeleteTag, "DeleteTag", pathParameters, null, null); + response.Clear(); + _logger.Information(Component, "Infisical tag deletion was successful."); + } + catch (Exception) + { + _logger.Error(Component, "Infisical tag deletion failed."); + throw; + } + } + + private static string FirstNonEmpty(params string[] values) + { + if (values == null) { return null; } + foreach (string value in values) { if (!string.IsNullOrEmpty(value)) { return value; } } + return null; + } + } +} diff --git a/src/PSInfisicalAPI/Tags/InfisicalTagDtos.cs b/src/PSInfisicalAPI/Tags/InfisicalTagDtos.cs new file mode 100644 index 0000000..622c80a --- /dev/null +++ b/src/PSInfisicalAPI/Tags/InfisicalTagDtos.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace PSInfisicalAPI.Tags +{ + internal sealed class InfisicalTagResponseDto + { + [JsonProperty("id")] public string Id { get; set; } + [JsonProperty("_id")] public string InternalId { get; set; } + [JsonProperty("slug")] public string Slug { get; set; } + [JsonProperty("name")] public string Name { get; set; } + [JsonProperty("color")] public string Color { get; set; } + [JsonProperty("projectId")] public string ProjectId { get; set; } + [JsonProperty("workspaceId")] public string WorkspaceId { get; set; } + [JsonProperty("createdAt")] public string CreatedAt { get; set; } + [JsonProperty("updatedAt")] public string UpdatedAt { get; set; } + } + + internal sealed class InfisicalTagListResponseDto + { + [JsonProperty("workspaceTags")] public List WorkspaceTags { get; set; } + [JsonProperty("tags")] public List Tags { get; set; } + } + + internal sealed class InfisicalTagSingleResponseDto + { + [JsonProperty("workspaceTag")] public InfisicalTagResponseDto WorkspaceTag { get; set; } + [JsonProperty("tag")] public InfisicalTagResponseDto Tag { get; set; } + } + + internal sealed class InfisicalTagCreateRequestDto + { + [JsonProperty("slug")] public string Slug { get; set; } + [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)] public string Name { get; set; } + [JsonProperty("color", NullValueHandling = NullValueHandling.Ignore)] public string Color { get; set; } + } + + internal sealed class InfisicalTagUpdateRequestDto + { + [JsonProperty("slug", NullValueHandling = NullValueHandling.Ignore)] public string Slug { get; set; } + [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)] public string Name { get; set; } + [JsonProperty("color", NullValueHandling = NullValueHandling.Ignore)] public string Color { get; set; } + } +} diff --git a/src/PSInfisicalAPI/Tags/InfisicalTagMapper.cs b/src/PSInfisicalAPI/Tags/InfisicalTagMapper.cs new file mode 100644 index 0000000..04ac855 --- /dev/null +++ b/src/PSInfisicalAPI/Tags/InfisicalTagMapper.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using PSInfisicalAPI.Models; + +namespace PSInfisicalAPI.Tags +{ + internal static class InfisicalTagMapper + { + public static InfisicalTag Map(InfisicalTagResponseDto dto, string fallbackProjectId) + { + if (dto == null) + { + return null; + } + + string projectId = !string.IsNullOrEmpty(dto.ProjectId) + ? dto.ProjectId + : (!string.IsNullOrEmpty(dto.WorkspaceId) ? dto.WorkspaceId : fallbackProjectId); + + return new InfisicalTag + { + Id = !string.IsNullOrEmpty(dto.Id) ? dto.Id : dto.InternalId, + Slug = dto.Slug, + Name = dto.Name, + Color = dto.Color, + ProjectId = projectId, + CreatedAtUtc = ParseTimestamp(dto.CreatedAt), + UpdatedAtUtc = ParseTimestamp(dto.UpdatedAt) + }; + } + + public static InfisicalTag[] MapMany(IEnumerable items, string fallbackProjectId) + { + if (items == null) + { + return Array.Empty(); + } + + List results = new List(); + foreach (InfisicalTagResponseDto dto in items) + { + InfisicalTag mapped = Map(dto, fallbackProjectId); + if (mapped != null) + { + results.Add(mapped); + } + } + + return results.ToArray(); + } + + 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; + } + } +} -- 2.52.0 From 3d93fb1173bc87e139d12fc7550343b000634614 Mon Sep 17 00:00:00 2001 From: GraceSolutions Date: Wed, 3 Jun 2026 17:30:29 -0400 Subject: [PATCH 07/12] M6: Secrets mutation - New/Update/Remove cmdlets + client methods + DTO tests --- build.ps1 | 5 +- .../SecretMutationDtoTests.cs | 73 +++++++++++ .../Cmdlets/NewInfisicalSecretCmdlet.cs | 73 +++++++++++ .../Cmdlets/RemoveInfisicalSecretCmdlet.cs | 56 ++++++++ .../Cmdlets/UpdateInfisicalSecretCmdlet.cs | 73 +++++++++++ .../Secrets/InfisicalSecretDtos.cs | 33 +++++ .../Secrets/InfisicalSecretQuery.cs | 39 ++++++ .../Secrets/InfisicalSecretsClient.cs | 123 ++++++++++++++++++ 8 files changed, 474 insertions(+), 1 deletion(-) create mode 100644 src/PSInfisicalAPI.Tests/SecretMutationDtoTests.cs create mode 100644 src/PSInfisicalAPI/Cmdlets/NewInfisicalSecretCmdlet.cs create mode 100644 src/PSInfisicalAPI/Cmdlets/RemoveInfisicalSecretCmdlet.cs create mode 100644 src/PSInfisicalAPI/Cmdlets/UpdateInfisicalSecretCmdlet.cs diff --git a/build.ps1 b/build.ps1 index 1591563..c1457a1 100644 --- a/build.ps1 +++ b/build.ps1 @@ -102,6 +102,9 @@ function Write-Manifest { 'Disconnect-Infisical', 'Get-InfisicalSecrets', 'Get-InfisicalSecret', + 'New-InfisicalSecret', + 'Update-InfisicalSecret', + 'Remove-InfisicalSecret', 'ConvertTo-InfisicalSecretDictionary', 'Export-InfisicalSecrets', 'Get-InfisicalProjects', @@ -183,7 +186,7 @@ if (`$null -eq `$manifest) { Import-Module -Name '$($ModuleDirectory.FullName)' -Force -`$cmds = @('Connect-Infisical','Disconnect-Infisical','Get-InfisicalSecrets','Get-InfisicalSecret','ConvertTo-InfisicalSecretDictionary','Export-InfisicalSecrets','Get-InfisicalProjects','Get-InfisicalProject','New-InfisicalProject','Update-InfisicalProject','Remove-InfisicalProject','Get-InfisicalEnvironments','Get-InfisicalEnvironment','New-InfisicalEnvironment','Update-InfisicalEnvironment','Remove-InfisicalEnvironment','Get-InfisicalFolders','Get-InfisicalFolder','New-InfisicalFolder','Update-InfisicalFolder','Remove-InfisicalFolder','Get-InfisicalTags','Get-InfisicalTag','New-InfisicalTag','Update-InfisicalTag','Remove-InfisicalTag') +`$cmds = @('Connect-Infisical','Disconnect-Infisical','Get-InfisicalSecrets','Get-InfisicalSecret','New-InfisicalSecret','Update-InfisicalSecret','Remove-InfisicalSecret','ConvertTo-InfisicalSecretDictionary','Export-InfisicalSecrets','Get-InfisicalProjects','Get-InfisicalProject','New-InfisicalProject','Update-InfisicalProject','Remove-InfisicalProject','Get-InfisicalEnvironments','Get-InfisicalEnvironment','New-InfisicalEnvironment','Update-InfisicalEnvironment','Remove-InfisicalEnvironment','Get-InfisicalFolders','Get-InfisicalFolder','New-InfisicalFolder','Update-InfisicalFolder','Remove-InfisicalFolder','Get-InfisicalTags','Get-InfisicalTag','New-InfisicalTag','Update-InfisicalTag','Remove-InfisicalTag') foreach (`$c in `$cmds) { if (-not (Get-Command -Name `$c -Module PSInfisicalAPI -ErrorAction SilentlyContinue)) { throw "Cmdlet not found: `$c" diff --git a/src/PSInfisicalAPI.Tests/SecretMutationDtoTests.cs b/src/PSInfisicalAPI.Tests/SecretMutationDtoTests.cs new file mode 100644 index 0000000..70d0700 --- /dev/null +++ b/src/PSInfisicalAPI.Tests/SecretMutationDtoTests.cs @@ -0,0 +1,73 @@ +using System.Reflection; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Xunit; + +namespace PSInfisicalAPI.Tests +{ + public class SecretMutationDtoTests + { + private static readonly System.Reflection.Assembly ModuleAssembly = + typeof(PSInfisicalAPI.Connections.InfisicalConnection).Assembly; + + private static object MakeDto(string typeName) + { + System.Type t = ModuleAssembly.GetType(typeName, true); + return System.Activator.CreateInstance(t); + } + + [Fact] + public void CreateRequestDto_Serializes_With_Expected_Field_Names() + { + object dto = MakeDto("PSInfisicalAPI.Secrets.InfisicalSecretCreateRequestDto"); + dto.GetType().GetProperty("WorkspaceId").SetValue(dto, "wks-1"); + dto.GetType().GetProperty("Environment").SetValue(dto, "prod"); + dto.GetType().GetProperty("SecretPath").SetValue(dto, "/db"); + dto.GetType().GetProperty("Type").SetValue(dto, "shared"); + dto.GetType().GetProperty("SecretValue").SetValue(dto, "p@ss"); + dto.GetType().GetProperty("SecretComment").SetValue(dto, "comment"); + + JObject json = JObject.Parse(JsonConvert.SerializeObject(dto)); + Assert.Equal("wks-1", (string)json["workspaceId"]); + Assert.Equal("prod", (string)json["environment"]); + Assert.Equal("/db", (string)json["secretPath"]); + Assert.Equal("shared", (string)json["type"]); + Assert.Equal("p@ss", (string)json["secretValue"]); + Assert.Equal("comment", (string)json["secretComment"]); + Assert.False(json.ContainsKey("skipMultilineEncoding")); + Assert.False(json.ContainsKey("tagIds")); + } + + [Fact] + public void UpdateRequestDto_Omits_Null_Optional_Fields() + { + object dto = MakeDto("PSInfisicalAPI.Secrets.InfisicalSecretUpdateRequestDto"); + dto.GetType().GetProperty("WorkspaceId").SetValue(dto, "wks-1"); + dto.GetType().GetProperty("Environment").SetValue(dto, "prod"); + dto.GetType().GetProperty("NewSecretName").SetValue(dto, "renamed"); + + JObject json = JObject.Parse(JsonConvert.SerializeObject(dto)); + Assert.Equal("renamed", (string)json["newSecretName"]); + Assert.False(json.ContainsKey("secretValue")); + Assert.False(json.ContainsKey("secretComment")); + Assert.False(json.ContainsKey("type")); + Assert.False(json.ContainsKey("secretPath")); + } + + [Fact] + public void DeleteRequestDto_Serializes_With_Expected_Field_Names() + { + object dto = MakeDto("PSInfisicalAPI.Secrets.InfisicalSecretDeleteRequestDto"); + dto.GetType().GetProperty("WorkspaceId").SetValue(dto, "wks-1"); + dto.GetType().GetProperty("Environment").SetValue(dto, "prod"); + dto.GetType().GetProperty("SecretPath").SetValue(dto, "/db"); + dto.GetType().GetProperty("Type").SetValue(dto, "shared"); + + JObject json = JObject.Parse(JsonConvert.SerializeObject(dto)); + Assert.Equal("wks-1", (string)json["workspaceId"]); + Assert.Equal("prod", (string)json["environment"]); + Assert.Equal("/db", (string)json["secretPath"]); + Assert.Equal("shared", (string)json["type"]); + } + } +} diff --git a/src/PSInfisicalAPI/Cmdlets/NewInfisicalSecretCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/NewInfisicalSecretCmdlet.cs new file mode 100644 index 0000000..4dd0e82 --- /dev/null +++ b/src/PSInfisicalAPI/Cmdlets/NewInfisicalSecretCmdlet.cs @@ -0,0 +1,73 @@ +using System; +using System.Management.Automation; +using System.Security; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Models; +using PSInfisicalAPI.Secrets; +using PSInfisicalAPI.Security; + +namespace PSInfisicalAPI.Cmdlets +{ + [Cmdlet(VerbsCommon.New, "InfisicalSecret", SupportsShouldProcess = true, DefaultParameterSetName = "PlainText")] + [OutputType(typeof(InfisicalSecret))] + public sealed class NewInfisicalSecretCmdlet : InfisicalCmdletBase + { + [Parameter(Mandatory = true, Position = 0)] public string SecretName { get; set; } + + [Parameter(Mandatory = true, Position = 1, ParameterSetName = "PlainText")] + public string SecretValue { get; set; } + + [Parameter(Mandatory = true, Position = 1, ParameterSetName = "SecureString")] + public SecureString SecureSecretValue { get; set; } + + [Parameter] public string SecretComment { get; set; } + [Parameter] public string ProjectId { get; set; } + [Parameter] public string Environment { get; set; } + [Parameter] public string SecretPath { get; set; } + [Parameter] public string ApiVersion { get; set; } + [Parameter] public InfisicalSecretType Type { get; set; } = InfisicalSecretType.Shared; + [Parameter] public SwitchParameter SkipMultilineEncoding { get; set; } + [Parameter] public string[] TagIds { get; set; } + + protected override void ProcessRecord() + { + try + { + if (!ShouldProcess(SecretName, "Create Infisical secret")) + { + return; + } + + string plainValue = SecureSecretValue != null + ? SecureStringUtility.UsePlainText(SecureSecretValue, p => p) + : SecretValue; + + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + InfisicalCreateSecretRequest request = new InfisicalCreateSecretRequest + { + SecretName = SecretName, + SecretValue = plainValue, + SecretComment = SecretComment, + ProjectId = ProjectId, + Environment = Environment, + SecretPath = SecretPath, + Type = Type.ToString(), + ApiVersion = ApiVersion, + SkipMultilineEncoding = SkipMultilineEncoding.IsPresent ? (bool?)true : null, + TagIds = TagIds + }; + + InfisicalSecretsClient client = new InfisicalSecretsClient(HttpClient, Logger); + InfisicalSecret secret = client.Create(connection, request); + if (secret != null) + { + WriteObject(secret); + } + } + catch (Exception exception) + { + ThrowTerminatingForException("NewInfisicalSecretCmdlet", "CreateSecret", exception); + } + } + } +} diff --git a/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalSecretCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalSecretCmdlet.cs new file mode 100644 index 0000000..3087596 --- /dev/null +++ b/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalSecretCmdlet.cs @@ -0,0 +1,56 @@ +using System; +using System.Management.Automation; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Models; +using PSInfisicalAPI.Secrets; + +namespace PSInfisicalAPI.Cmdlets +{ + [Cmdlet(VerbsCommon.Remove, "InfisicalSecret", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)] + public sealed class RemoveInfisicalSecretCmdlet : InfisicalCmdletBase + { + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)] + public string SecretName { get; set; } + + [Parameter] public string ProjectId { get; set; } + [Parameter] public string Environment { get; set; } + [Parameter] public string SecretPath { get; set; } + [Parameter] public string ApiVersion { get; set; } + [Parameter] public InfisicalSecretType Type { get; set; } = InfisicalSecretType.Shared; + [Parameter] public SwitchParameter PassThru { get; set; } + + protected override void ProcessRecord() + { + try + { + if (!ShouldProcess(SecretName, "Remove Infisical secret")) + { + return; + } + + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + InfisicalDeleteSecretRequest request = new InfisicalDeleteSecretRequest + { + SecretName = SecretName, + ProjectId = ProjectId, + Environment = Environment, + SecretPath = SecretPath, + Type = Type.ToString(), + ApiVersion = ApiVersion + }; + + InfisicalSecretsClient client = new InfisicalSecretsClient(HttpClient, Logger); + client.Delete(connection, request); + + if (PassThru.IsPresent) + { + WriteObject(SecretName); + } + } + catch (Exception exception) + { + ThrowTerminatingForException("RemoveInfisicalSecretCmdlet", "DeleteSecret", exception); + } + } + } +} diff --git a/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalSecretCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalSecretCmdlet.cs new file mode 100644 index 0000000..359025f --- /dev/null +++ b/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalSecretCmdlet.cs @@ -0,0 +1,73 @@ +using System; +using System.Management.Automation; +using System.Security; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Models; +using PSInfisicalAPI.Secrets; +using PSInfisicalAPI.Security; + +namespace PSInfisicalAPI.Cmdlets +{ + [Cmdlet(VerbsData.Update, "InfisicalSecret", SupportsShouldProcess = true, DefaultParameterSetName = "PlainText")] + [OutputType(typeof(InfisicalSecret))] + public sealed class UpdateInfisicalSecretCmdlet : InfisicalCmdletBase + { + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)] + public string SecretName { get; set; } + + [Parameter(ParameterSetName = "PlainText")] public string SecretValue { get; set; } + [Parameter(ParameterSetName = "SecureString")] public SecureString SecureSecretValue { get; set; } + + [Parameter] public string NewSecretName { get; set; } + [Parameter] public string SecretComment { get; set; } + [Parameter] public string ProjectId { get; set; } + [Parameter] public string Environment { get; set; } + [Parameter] public string SecretPath { get; set; } + [Parameter] public string ApiVersion { get; set; } + [Parameter] public InfisicalSecretType Type { get; set; } = InfisicalSecretType.Shared; + [Parameter] public SwitchParameter SkipMultilineEncoding { get; set; } + [Parameter] public string[] TagIds { get; set; } + + protected override void ProcessRecord() + { + try + { + if (!ShouldProcess(SecretName, "Update Infisical secret")) + { + return; + } + + string plainValue = SecureSecretValue != null + ? SecureStringUtility.UsePlainText(SecureSecretValue, p => p) + : SecretValue; + + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + InfisicalUpdateSecretRequest request = new InfisicalUpdateSecretRequest + { + SecretName = SecretName, + NewSecretName = NewSecretName, + SecretValue = plainValue, + SecretComment = SecretComment, + ProjectId = ProjectId, + Environment = Environment, + SecretPath = SecretPath, + Type = Type.ToString(), + ApiVersion = ApiVersion, + SkipMultilineEncoding = SkipMultilineEncoding.IsPresent ? (bool?)true : null, + TagIds = TagIds + }; + + InfisicalSecretsClient client = new InfisicalSecretsClient(HttpClient, Logger); + InfisicalSecret secret = client.Update(connection, request); + if (secret != null) + { + WriteObject(secret); + } + } + catch (Exception exception) + { + ThrowTerminatingForException("UpdateInfisicalSecretCmdlet", "UpdateSecret", exception); + } + } + } +} diff --git a/src/PSInfisicalAPI/Secrets/InfisicalSecretDtos.cs b/src/PSInfisicalAPI/Secrets/InfisicalSecretDtos.cs index 83f8eaf..8d4ea3a 100644 --- a/src/PSInfisicalAPI/Secrets/InfisicalSecretDtos.cs +++ b/src/PSInfisicalAPI/Secrets/InfisicalSecretDtos.cs @@ -55,4 +55,37 @@ namespace PSInfisicalAPI.Secrets { [JsonProperty("secret")] public InfisicalSecretResponseDto Secret { get; set; } } + + internal sealed class InfisicalSecretCreateRequestDto + { + [JsonProperty("workspaceId")] public string WorkspaceId { get; set; } + [JsonProperty("environment")] public string Environment { get; set; } + [JsonProperty("secretPath", NullValueHandling = NullValueHandling.Ignore)] public string SecretPath { get; set; } + [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)] public string Type { get; set; } + [JsonProperty("secretValue")] public string SecretValue { get; set; } + [JsonProperty("secretComment", NullValueHandling = NullValueHandling.Ignore)] public string SecretComment { get; set; } + [JsonProperty("skipMultilineEncoding", NullValueHandling = NullValueHandling.Ignore)] public bool? SkipMultilineEncoding { get; set; } + [JsonProperty("tagIds", NullValueHandling = NullValueHandling.Ignore)] public string[] TagIds { get; set; } + } + + internal sealed class InfisicalSecretUpdateRequestDto + { + [JsonProperty("workspaceId")] public string WorkspaceId { get; set; } + [JsonProperty("environment")] public string Environment { get; set; } + [JsonProperty("secretPath", NullValueHandling = NullValueHandling.Ignore)] public string SecretPath { get; set; } + [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)] public string Type { get; set; } + [JsonProperty("secretValue", NullValueHandling = NullValueHandling.Ignore)] public string SecretValue { get; set; } + [JsonProperty("secretComment", NullValueHandling = NullValueHandling.Ignore)] public string SecretComment { get; set; } + [JsonProperty("newSecretName", NullValueHandling = NullValueHandling.Ignore)] public string NewSecretName { get; set; } + [JsonProperty("skipMultilineEncoding", NullValueHandling = NullValueHandling.Ignore)] public bool? SkipMultilineEncoding { get; set; } + [JsonProperty("tagIds", NullValueHandling = NullValueHandling.Ignore)] public string[] TagIds { get; set; } + } + + internal sealed class InfisicalSecretDeleteRequestDto + { + [JsonProperty("workspaceId")] public string WorkspaceId { get; set; } + [JsonProperty("environment")] public string Environment { get; set; } + [JsonProperty("secretPath", NullValueHandling = NullValueHandling.Ignore)] public string SecretPath { get; set; } + [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)] public string Type { get; set; } + } } diff --git a/src/PSInfisicalAPI/Secrets/InfisicalSecretQuery.cs b/src/PSInfisicalAPI/Secrets/InfisicalSecretQuery.cs index 8ede056..a5e20d1 100644 --- a/src/PSInfisicalAPI/Secrets/InfisicalSecretQuery.cs +++ b/src/PSInfisicalAPI/Secrets/InfisicalSecretQuery.cs @@ -30,4 +30,43 @@ namespace PSInfisicalAPI.Secrets public bool? ExpandSecretReferences { get; set; } public bool? IncludeImports { get; set; } } + + public sealed class InfisicalCreateSecretRequest + { + public string SecretName { get; set; } + public string SecretValue { get; set; } + public string SecretComment { get; set; } + public string ProjectId { get; set; } + public string Environment { get; set; } + public string SecretPath { get; set; } + public string Type { get; set; } + public string ApiVersion { get; set; } + public bool? SkipMultilineEncoding { get; set; } + public string[] TagIds { get; set; } + } + + public sealed class InfisicalUpdateSecretRequest + { + public string SecretName { get; set; } + public string NewSecretName { get; set; } + public string SecretValue { get; set; } + public string SecretComment { get; set; } + public string ProjectId { get; set; } + public string Environment { get; set; } + public string SecretPath { get; set; } + public string Type { get; set; } + public string ApiVersion { get; set; } + public bool? SkipMultilineEncoding { get; set; } + public string[] TagIds { get; set; } + } + + public sealed class InfisicalDeleteSecretRequest + { + public string SecretName { get; set; } + public string ProjectId { get; set; } + public string Environment { get; set; } + public string SecretPath { get; set; } + public string Type { get; set; } + public string ApiVersion { get; set; } + } } diff --git a/src/PSInfisicalAPI/Secrets/InfisicalSecretsClient.cs b/src/PSInfisicalAPI/Secrets/InfisicalSecretsClient.cs index aa9d7a7..2f62246 100644 --- a/src/PSInfisicalAPI/Secrets/InfisicalSecretsClient.cs +++ b/src/PSInfisicalAPI/Secrets/InfisicalSecretsClient.cs @@ -136,6 +136,129 @@ namespace PSInfisicalAPI.Secrets } } + public InfisicalSecret Create(InfisicalConnection connection, InfisicalCreateSecretRequest request) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + if (request == null) { throw new ArgumentNullException(nameof(request)); } + if (string.IsNullOrEmpty(request.SecretName)) { throw new InfisicalConfigurationException("SecretName is required."); } + if (request.SecretValue == null) { throw new InfisicalConfigurationException("SecretValue is required."); } + + string resolvedProjectId = FirstNonEmpty(request.ProjectId, connection.ProjectId); + string resolvedEnvironment = FirstNonEmpty(request.Environment, connection.Environment); + if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); } + if (string.IsNullOrEmpty(resolvedEnvironment)) { throw new InfisicalConfigurationException("Environment is required."); } + + Dictionary pathParameters = new Dictionary { { "secretName", request.SecretName } }; + InfisicalSecretCreateRequestDto dtoRequest = new InfisicalSecretCreateRequestDto + { + WorkspaceId = resolvedProjectId, + Environment = resolvedEnvironment, + SecretPath = FirstNonEmpty(request.SecretPath, connection.DefaultSecretPath, "/"), + Type = string.IsNullOrEmpty(request.Type) ? "shared" : request.Type.ToLowerInvariant(), + SecretValue = request.SecretValue, + SecretComment = request.SecretComment, + SkipMultilineEncoding = request.SkipMultilineEncoding, + TagIds = request.TagIds + }; + string body = _serializer.Serialize(dtoRequest); + + try + { + _logger.Information(Component, string.Concat("Attempting to create Infisical secret '", request.SecretName, "'. Please Wait...")); + InfisicalHttpResponse response = SendWithVersionFallback(connection, InfisicalEndpointNames.CreateSecret, request.ApiVersion, "CreateSecret", pathParameters, null, body); + InfisicalSecretSingleResponseDto dto = _serializer.Deserialize(response.Body); + response.Clear(); + + InfisicalSecret mapped = InfisicalSecretMapper.Map(dto != null ? dto.Secret : null); + _logger.Information(Component, "Infisical secret creation was successful."); + return mapped; + } + catch (Exception) + { + _logger.Error(Component, "Infisical secret creation failed."); + throw; + } + } + + public InfisicalSecret Update(InfisicalConnection connection, InfisicalUpdateSecretRequest request) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + if (request == null) { throw new ArgumentNullException(nameof(request)); } + if (string.IsNullOrEmpty(request.SecretName)) { throw new InfisicalConfigurationException("SecretName is required."); } + + string resolvedProjectId = FirstNonEmpty(request.ProjectId, connection.ProjectId); + string resolvedEnvironment = FirstNonEmpty(request.Environment, connection.Environment); + if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); } + if (string.IsNullOrEmpty(resolvedEnvironment)) { throw new InfisicalConfigurationException("Environment is required."); } + + Dictionary pathParameters = new Dictionary { { "secretName", request.SecretName } }; + InfisicalSecretUpdateRequestDto dtoRequest = new InfisicalSecretUpdateRequestDto + { + WorkspaceId = resolvedProjectId, + Environment = resolvedEnvironment, + SecretPath = FirstNonEmpty(request.SecretPath, connection.DefaultSecretPath, "/"), + Type = string.IsNullOrEmpty(request.Type) ? "shared" : request.Type.ToLowerInvariant(), + SecretValue = request.SecretValue, + SecretComment = request.SecretComment, + NewSecretName = request.NewSecretName, + SkipMultilineEncoding = request.SkipMultilineEncoding, + TagIds = request.TagIds + }; + string body = _serializer.Serialize(dtoRequest); + + try + { + _logger.Information(Component, string.Concat("Attempting to update Infisical secret '", request.SecretName, "'. Please Wait...")); + InfisicalHttpResponse response = SendWithVersionFallback(connection, InfisicalEndpointNames.UpdateSecret, request.ApiVersion, "UpdateSecret", pathParameters, null, body); + InfisicalSecretSingleResponseDto dto = _serializer.Deserialize(response.Body); + response.Clear(); + + InfisicalSecret mapped = InfisicalSecretMapper.Map(dto != null ? dto.Secret : null); + _logger.Information(Component, "Infisical secret update was successful."); + return mapped; + } + catch (Exception) + { + _logger.Error(Component, "Infisical secret update failed."); + throw; + } + } + + public void Delete(InfisicalConnection connection, InfisicalDeleteSecretRequest request) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + if (request == null) { throw new ArgumentNullException(nameof(request)); } + if (string.IsNullOrEmpty(request.SecretName)) { throw new InfisicalConfigurationException("SecretName is required."); } + + string resolvedProjectId = FirstNonEmpty(request.ProjectId, connection.ProjectId); + string resolvedEnvironment = FirstNonEmpty(request.Environment, connection.Environment); + if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); } + if (string.IsNullOrEmpty(resolvedEnvironment)) { throw new InfisicalConfigurationException("Environment is required."); } + + Dictionary pathParameters = new Dictionary { { "secretName", request.SecretName } }; + InfisicalSecretDeleteRequestDto dtoRequest = new InfisicalSecretDeleteRequestDto + { + WorkspaceId = resolvedProjectId, + Environment = resolvedEnvironment, + SecretPath = FirstNonEmpty(request.SecretPath, connection.DefaultSecretPath, "/"), + Type = string.IsNullOrEmpty(request.Type) ? "shared" : request.Type.ToLowerInvariant() + }; + string body = _serializer.Serialize(dtoRequest); + + try + { + _logger.Information(Component, string.Concat("Attempting to delete Infisical secret '", request.SecretName, "'. Please Wait...")); + InfisicalHttpResponse response = SendWithVersionFallback(connection, InfisicalEndpointNames.DeleteSecret, request.ApiVersion, "DeleteSecret", pathParameters, null, body); + response.Clear(); + _logger.Information(Component, "Infisical secret deletion was successful."); + } + catch (Exception) + { + _logger.Error(Component, "Infisical secret deletion failed."); + throw; + } + } + private InfisicalHttpResponse SendWithVersionFallback( InfisicalConnection connection, string endpointName, -- 2.52.0 From d9822aab7a4a19f35928469dcb56925255625608 Mon Sep 17 00:00:00 2001 From: GraceSolutions Date: Wed, 3 Jun 2026 17:36:34 -0400 Subject: [PATCH 08/12] M7: Auth providers - JWT/OIDC/LDAP/Azure/GCP IAM via Connect-Infisical parameter sets --- src/PSInfisicalAPI.Tests/AuthProviderTests.cs | 157 ++++++++++++++++++ .../EndpointRegistryTests.cs | 2 - .../Authentication/AzureAuthProvider.cs | 43 +++++ .../Authentication/GcpIamAuthProvider.cs | 43 +++++ .../Authentication/IdentityLoginExecutor.cs | 101 +++++++++++ .../InfisicalAuthenticationRequest.cs | 5 + .../Authentication/JwtAuthProvider.cs | 43 +++++ .../Authentication/LdapAuthProvider.cs | 48 ++++++ .../Authentication/OidcAuthProvider.cs | 43 +++++ .../Cmdlets/ConnectInfisicalCmdlet.cs | 138 ++++++++++++--- .../Endpoints/InfisicalEndpointNames.cs | 2 - .../Endpoints/InfisicalEndpointRegistry.cs | 24 --- .../Models/InfisicalAuthType.cs | 7 +- 13 files changed, 602 insertions(+), 54 deletions(-) create mode 100644 src/PSInfisicalAPI.Tests/AuthProviderTests.cs create mode 100644 src/PSInfisicalAPI/Authentication/AzureAuthProvider.cs create mode 100644 src/PSInfisicalAPI/Authentication/GcpIamAuthProvider.cs create mode 100644 src/PSInfisicalAPI/Authentication/IdentityLoginExecutor.cs create mode 100644 src/PSInfisicalAPI/Authentication/JwtAuthProvider.cs create mode 100644 src/PSInfisicalAPI/Authentication/LdapAuthProvider.cs create mode 100644 src/PSInfisicalAPI/Authentication/OidcAuthProvider.cs diff --git a/src/PSInfisicalAPI.Tests/AuthProviderTests.cs b/src/PSInfisicalAPI.Tests/AuthProviderTests.cs new file mode 100644 index 0000000..51c2b13 --- /dev/null +++ b/src/PSInfisicalAPI.Tests/AuthProviderTests.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json.Linq; +using PSInfisicalAPI.Authentication; +using PSInfisicalAPI.Errors; +using PSInfisicalAPI.Http; +using PSInfisicalAPI.Security; +using Xunit; + +namespace PSInfisicalAPI.Tests +{ + public class AuthProviderTests + { + private sealed class CapturingHttpClient : IInfisicalHttpClient + { + public InfisicalHttpRequest CapturedRequest { get; private set; } + public string ResponseBody { get; set; } = "{\"accessToken\":\"abc.def.ghi\",\"expiresIn\":3600,\"tokenType\":\"Bearer\"}"; + + public InfisicalHttpResponse Send(InfisicalHttpRequest request) + { + CapturedRequest = request; + return new InfisicalHttpResponse + { + StatusCode = 200, + Body = ResponseBody, + Headers = new Dictionary() + }; + } + } + + private static InfisicalAuthenticationRequest BaseRequest() + { + return new InfisicalAuthenticationRequest + { + BaseUri = new Uri("https://example.invalid"), + ApiVersion = "v1" + }; + } + + [Fact] + public void JwtAuthProvider_Posts_IdentityId_And_Jwt() + { + CapturingHttpClient http = new CapturingHttpClient(); + InfisicalAuthenticationRequest request = BaseRequest(); + request.IdentityId = "identity-1"; + request.Jwt = SecureStringUtility.ToReadOnlySecureString("token.value"); + + InfisicalAuthenticationResult result = new JwtAuthProvider().Authenticate(request, http, null); + + Assert.NotNull(result); + Assert.NotNull(http.CapturedRequest); + Assert.Equal("POST", http.CapturedRequest.Method); + Assert.EndsWith("/api/v1/auth/jwt-auth/login", http.CapturedRequest.Uri.AbsolutePath); + JObject body = JObject.Parse(http.CapturedRequest.Body); + Assert.Equal("identity-1", (string)body["identityId"]); + Assert.Equal("token.value", (string)body["jwt"]); + } + + [Fact] + public void OidcAuthProvider_Posts_IdentityId_And_Jwt_To_Oidc_Endpoint() + { + CapturingHttpClient http = new CapturingHttpClient(); + InfisicalAuthenticationRequest request = BaseRequest(); + request.IdentityId = "identity-2"; + request.Jwt = SecureStringUtility.ToReadOnlySecureString("oidc.token"); + + new OidcAuthProvider().Authenticate(request, http, null); + + Assert.EndsWith("/api/v1/auth/oidc-auth/login", http.CapturedRequest.Uri.AbsolutePath); + JObject body = JObject.Parse(http.CapturedRequest.Body); + Assert.Equal("identity-2", (string)body["identityId"]); + Assert.Equal("oidc.token", (string)body["jwt"]); + } + + [Fact] + public void LdapAuthProvider_Posts_Username_And_Password_To_Ldap_Endpoint() + { + CapturingHttpClient http = new CapturingHttpClient(); + InfisicalAuthenticationRequest request = BaseRequest(); + request.Username = "svc.account"; + request.Password = SecureStringUtility.ToReadOnlySecureString("P@ssw0rd!"); + + new LdapAuthProvider().Authenticate(request, http, null); + + Assert.EndsWith("/api/v1/auth/ldap-auth/login", http.CapturedRequest.Uri.AbsolutePath); + JObject body = JObject.Parse(http.CapturedRequest.Body); + Assert.Equal("svc.account", (string)body["username"]); + Assert.Equal("P@ssw0rd!", (string)body["password"]); + Assert.False(body.ContainsKey("identityId")); + } + + [Fact] + public void LdapAuthProvider_Includes_IdentityId_When_Supplied() + { + CapturingHttpClient http = new CapturingHttpClient(); + InfisicalAuthenticationRequest request = BaseRequest(); + request.Username = "u"; + request.Password = SecureStringUtility.ToReadOnlySecureString("p"); + request.IdentityId = "id-ldap"; + + new LdapAuthProvider().Authenticate(request, http, null); + + JObject body = JObject.Parse(http.CapturedRequest.Body); + Assert.Equal("id-ldap", (string)body["identityId"]); + } + + [Fact] + public void AzureAuthProvider_Posts_IdentityId_And_Jwt_To_Azure_Endpoint() + { + CapturingHttpClient http = new CapturingHttpClient(); + InfisicalAuthenticationRequest request = BaseRequest(); + request.IdentityId = "identity-az"; + request.Jwt = SecureStringUtility.ToReadOnlySecureString("az.token"); + + new AzureAuthProvider().Authenticate(request, http, null); + + Assert.EndsWith("/api/v1/auth/azure-auth/login", http.CapturedRequest.Uri.AbsolutePath); + JObject body = JObject.Parse(http.CapturedRequest.Body); + Assert.Equal("identity-az", (string)body["identityId"]); + Assert.Equal("az.token", (string)body["jwt"]); + } + + [Fact] + public void GcpIamAuthProvider_Posts_IdentityId_And_Jwt_To_Gcp_Endpoint() + { + CapturingHttpClient http = new CapturingHttpClient(); + InfisicalAuthenticationRequest request = BaseRequest(); + request.IdentityId = "identity-gcp"; + request.Jwt = SecureStringUtility.ToReadOnlySecureString("gcp.token"); + + new GcpIamAuthProvider().Authenticate(request, http, null); + + Assert.EndsWith("/api/v1/auth/gcp-auth/login", http.CapturedRequest.Uri.AbsolutePath); + JObject body = JObject.Parse(http.CapturedRequest.Body); + Assert.Equal("identity-gcp", (string)body["identityId"]); + Assert.Equal("gcp.token", (string)body["jwt"]); + } + + [Fact] + public void JwtAuthProvider_Throws_When_IdentityId_Missing() + { + InfisicalAuthenticationRequest request = BaseRequest(); + request.Jwt = SecureStringUtility.ToReadOnlySecureString("x"); + Assert.Throws(() => + new JwtAuthProvider().Authenticate(request, new CapturingHttpClient(), null)); + } + + [Fact] + public void LdapAuthProvider_Throws_When_Password_Missing() + { + InfisicalAuthenticationRequest request = BaseRequest(); + request.Username = "u"; + Assert.Throws(() => + new LdapAuthProvider().Authenticate(request, new CapturingHttpClient(), null)); + } + } +} diff --git a/src/PSInfisicalAPI.Tests/EndpointRegistryTests.cs b/src/PSInfisicalAPI.Tests/EndpointRegistryTests.cs index 1edada8..adeccf3 100644 --- a/src/PSInfisicalAPI.Tests/EndpointRegistryTests.cs +++ b/src/PSInfisicalAPI.Tests/EndpointRegistryTests.cs @@ -69,8 +69,6 @@ namespace PSInfisicalAPI.Tests [InlineData(InfisicalEndpointNames.JwtAuthLogin, "POST", "/api/v1/auth/jwt-auth/login")] [InlineData(InfisicalEndpointNames.OidcAuthLogin, "POST", "/api/v1/auth/oidc-auth/login")] [InlineData(InfisicalEndpointNames.LdapAuthLogin, "POST", "/api/v1/auth/ldap-auth/login")] - [InlineData(InfisicalEndpointNames.KubernetesAuthLogin, "POST", "/api/v1/auth/kubernetes-auth/login")] - [InlineData(InfisicalEndpointNames.AwsAuthLogin, "POST", "/api/v1/auth/aws-auth/login")] [InlineData(InfisicalEndpointNames.AzureAuthLogin, "POST", "/api/v1/auth/azure-auth/login")] [InlineData(InfisicalEndpointNames.GcpIamAuthLogin, "POST", "/api/v1/auth/gcp-auth/login")] public void Registered_Endpoints_Have_Expected_Shape(string name, string method, string template) diff --git a/src/PSInfisicalAPI/Authentication/AzureAuthProvider.cs b/src/PSInfisicalAPI/Authentication/AzureAuthProvider.cs new file mode 100644 index 0000000..9d9b53a --- /dev/null +++ b/src/PSInfisicalAPI/Authentication/AzureAuthProvider.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using PSInfisicalAPI.Endpoints; +using PSInfisicalAPI.Errors; +using PSInfisicalAPI.Http; +using PSInfisicalAPI.Logging; +using PSInfisicalAPI.Security; + +namespace PSInfisicalAPI.Authentication +{ + public sealed class AzureAuthProvider : IInfisicalAuthProvider + { + private const string Component = "AzureAuthProvider"; + + public string Name { get { return "AzureAuth"; } } + + public InfisicalAuthenticationResult Authenticate(InfisicalAuthenticationRequest request, IInfisicalHttpClient httpClient, IInfisicalLogger logger) + { + if (request == null || string.IsNullOrEmpty(request.IdentityId)) + { + throw new InfisicalAuthenticationException("IdentityId is required for Azure Auth."); + } + + if (request.Jwt == null || request.Jwt.Length == 0) + { + throw new InfisicalAuthenticationException("Jwt is required for Azure Auth."); + } + + return IdentityLoginExecutor.Execute(InfisicalEndpointNames.AzureAuthLogin, Component, request, httpClient, logger, serializer => + { + return SecureStringUtility.UsePlainText(request.Jwt, plainJwt => + { + Dictionary bodyObject = new Dictionary + { + { "identityId", request.IdentityId }, + { "jwt", plainJwt ?? string.Empty } + }; + + return serializer.Serialize(bodyObject); + }); + }); + } + } +} diff --git a/src/PSInfisicalAPI/Authentication/GcpIamAuthProvider.cs b/src/PSInfisicalAPI/Authentication/GcpIamAuthProvider.cs new file mode 100644 index 0000000..3423ea6 --- /dev/null +++ b/src/PSInfisicalAPI/Authentication/GcpIamAuthProvider.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using PSInfisicalAPI.Endpoints; +using PSInfisicalAPI.Errors; +using PSInfisicalAPI.Http; +using PSInfisicalAPI.Logging; +using PSInfisicalAPI.Security; + +namespace PSInfisicalAPI.Authentication +{ + public sealed class GcpIamAuthProvider : IInfisicalAuthProvider + { + private const string Component = "GcpIamAuthProvider"; + + public string Name { get { return "GcpIamAuth"; } } + + public InfisicalAuthenticationResult Authenticate(InfisicalAuthenticationRequest request, IInfisicalHttpClient httpClient, IInfisicalLogger logger) + { + if (request == null || string.IsNullOrEmpty(request.IdentityId)) + { + throw new InfisicalAuthenticationException("IdentityId is required for GCP IAM Auth."); + } + + if (request.Jwt == null || request.Jwt.Length == 0) + { + throw new InfisicalAuthenticationException("Jwt is required for GCP IAM Auth."); + } + + return IdentityLoginExecutor.Execute(InfisicalEndpointNames.GcpIamAuthLogin, Component, request, httpClient, logger, serializer => + { + return SecureStringUtility.UsePlainText(request.Jwt, plainJwt => + { + Dictionary bodyObject = new Dictionary + { + { "identityId", request.IdentityId }, + { "jwt", plainJwt ?? string.Empty } + }; + + return serializer.Serialize(bodyObject); + }); + }); + } + } +} diff --git a/src/PSInfisicalAPI/Authentication/IdentityLoginExecutor.cs b/src/PSInfisicalAPI/Authentication/IdentityLoginExecutor.cs new file mode 100644 index 0000000..1e3baf0 --- /dev/null +++ b/src/PSInfisicalAPI/Authentication/IdentityLoginExecutor.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Security; +using PSInfisicalAPI.Endpoints; +using PSInfisicalAPI.Errors; +using PSInfisicalAPI.Http; +using PSInfisicalAPI.Logging; +using PSInfisicalAPI.Security; +using PSInfisicalAPI.Serialization; + +namespace PSInfisicalAPI.Authentication +{ + internal static class IdentityLoginExecutor + { + internal static InfisicalAuthenticationResult Execute( + string endpointName, + string component, + InfisicalAuthenticationRequest request, + IInfisicalHttpClient httpClient, + IInfisicalLogger logger, + Func bodyFactory) + { + if (request == null) { throw new ArgumentNullException(nameof(request)); } + if (httpClient == null) { throw new ArgumentNullException(nameof(httpClient)); } + if (bodyFactory == null) { throw new ArgumentNullException(nameof(bodyFactory)); } + + IInfisicalLogger log = logger ?? NullInfisicalLogger.Instance; + log.Information(component, "Attempting to authenticate to Infisical. Please Wait..."); + + InfisicalEndpointDefinition definition = InfisicalEndpointRegistry.Get(endpointName); + Uri uri = InfisicalUriBuilder.Build(request.BaseUri, definition, null, null); + JsonInfisicalSerializer serializer = new JsonInfisicalSerializer(); + string body = bodyFactory(serializer); + + InfisicalHttpRequest httpRequest = new InfisicalHttpRequest + { + OperationName = "Authenticate", + EndpointName = definition.Name, + Method = definition.Method, + Uri = uri, + Body = body, + ContentType = "application/json", + ContainsSecretMaterialInRequest = definition.ContainsSecretMaterialInRequest, + ContainsSecretMaterialInResponse = definition.ContainsSecretMaterialInResponse, + Headers = new Dictionary { { "Accept", "application/json" } } + }; + + InfisicalHttpResponse response = httpClient.Send(httpRequest); + + try + { + if (response.StatusCode < 200 || response.StatusCode >= 300) + { + log.Error(component, "Infisical authentication failed."); + throw new InfisicalAuthenticationException(string.Concat(component, " login returned status ", response.StatusCode.ToString(CultureInfo.InvariantCulture), ".")); + } + + IdentityLoginResponse parsed = serializer.Deserialize(response.Body); + if (parsed == null || string.IsNullOrEmpty(parsed.AccessToken)) + { + throw new InfisicalAuthenticationException(string.Concat(component, " login response did not contain an access token.")); + } + + SecureString accessToken = SecureStringUtility.ToReadOnlySecureString(parsed.AccessToken); + + DateTimeOffset? expiresAt = null; + if (parsed.ExpiresIn > 0) + { + expiresAt = DateTimeOffset.UtcNow.AddSeconds(parsed.ExpiresIn); + } + + parsed.AccessToken = null; + + log.Information(component, "Infisical authentication was successful."); + return new InfisicalAuthenticationResult + { + AccessToken = accessToken, + TokenType = string.IsNullOrEmpty(parsed.TokenType) ? "Bearer" : parsed.TokenType, + ExpiresAtUtc = expiresAt + }; + } + finally + { + response.Clear(); + } + } + + private sealed class IdentityLoginResponse + { + [Newtonsoft.Json.JsonProperty("accessToken")] + public string AccessToken { get; set; } + + [Newtonsoft.Json.JsonProperty("expiresIn")] + public int ExpiresIn { get; set; } + + [Newtonsoft.Json.JsonProperty("tokenType")] + public string TokenType { get; set; } + } + } +} diff --git a/src/PSInfisicalAPI/Authentication/InfisicalAuthenticationRequest.cs b/src/PSInfisicalAPI/Authentication/InfisicalAuthenticationRequest.cs index f2c5be8..6fecb09 100644 --- a/src/PSInfisicalAPI/Authentication/InfisicalAuthenticationRequest.cs +++ b/src/PSInfisicalAPI/Authentication/InfisicalAuthenticationRequest.cs @@ -10,5 +10,10 @@ namespace PSInfisicalAPI.Authentication public string ClientId { get; set; } public SecureString ClientSecret { get; set; } public SecureString PreSuppliedAccessToken { get; set; } + + public string IdentityId { get; set; } + public SecureString Jwt { get; set; } + public string Username { get; set; } + public SecureString Password { get; set; } } } diff --git a/src/PSInfisicalAPI/Authentication/JwtAuthProvider.cs b/src/PSInfisicalAPI/Authentication/JwtAuthProvider.cs new file mode 100644 index 0000000..e9ee4e8 --- /dev/null +++ b/src/PSInfisicalAPI/Authentication/JwtAuthProvider.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using PSInfisicalAPI.Endpoints; +using PSInfisicalAPI.Errors; +using PSInfisicalAPI.Http; +using PSInfisicalAPI.Logging; +using PSInfisicalAPI.Security; + +namespace PSInfisicalAPI.Authentication +{ + public sealed class JwtAuthProvider : IInfisicalAuthProvider + { + private const string Component = "JwtAuthProvider"; + + public string Name { get { return "JwtAuth"; } } + + public InfisicalAuthenticationResult Authenticate(InfisicalAuthenticationRequest request, IInfisicalHttpClient httpClient, IInfisicalLogger logger) + { + if (request == null || string.IsNullOrEmpty(request.IdentityId)) + { + throw new InfisicalAuthenticationException("IdentityId is required for JWT Auth."); + } + + if (request.Jwt == null || request.Jwt.Length == 0) + { + throw new InfisicalAuthenticationException("Jwt is required for JWT Auth."); + } + + return IdentityLoginExecutor.Execute(InfisicalEndpointNames.JwtAuthLogin, Component, request, httpClient, logger, serializer => + { + return SecureStringUtility.UsePlainText(request.Jwt, plainJwt => + { + Dictionary bodyObject = new Dictionary + { + { "identityId", request.IdentityId }, + { "jwt", plainJwt ?? string.Empty } + }; + + return serializer.Serialize(bodyObject); + }); + }); + } + } +} diff --git a/src/PSInfisicalAPI/Authentication/LdapAuthProvider.cs b/src/PSInfisicalAPI/Authentication/LdapAuthProvider.cs new file mode 100644 index 0000000..d670dd3 --- /dev/null +++ b/src/PSInfisicalAPI/Authentication/LdapAuthProvider.cs @@ -0,0 +1,48 @@ +using System.Collections.Generic; +using PSInfisicalAPI.Endpoints; +using PSInfisicalAPI.Errors; +using PSInfisicalAPI.Http; +using PSInfisicalAPI.Logging; +using PSInfisicalAPI.Security; + +namespace PSInfisicalAPI.Authentication +{ + public sealed class LdapAuthProvider : IInfisicalAuthProvider + { + private const string Component = "LdapAuthProvider"; + + public string Name { get { return "LdapAuth"; } } + + public InfisicalAuthenticationResult Authenticate(InfisicalAuthenticationRequest request, IInfisicalHttpClient httpClient, IInfisicalLogger logger) + { + if (request == null || string.IsNullOrEmpty(request.Username)) + { + throw new InfisicalAuthenticationException("Username is required for LDAP Auth."); + } + + if (request.Password == null || request.Password.Length == 0) + { + throw new InfisicalAuthenticationException("Password is required for LDAP Auth."); + } + + return IdentityLoginExecutor.Execute(InfisicalEndpointNames.LdapAuthLogin, Component, request, httpClient, logger, serializer => + { + return SecureStringUtility.UsePlainText(request.Password, plainPassword => + { + Dictionary bodyObject = new Dictionary + { + { "username", request.Username }, + { "password", plainPassword ?? string.Empty } + }; + + if (!string.IsNullOrEmpty(request.IdentityId)) + { + bodyObject["identityId"] = request.IdentityId; + } + + return serializer.Serialize(bodyObject); + }); + }); + } + } +} diff --git a/src/PSInfisicalAPI/Authentication/OidcAuthProvider.cs b/src/PSInfisicalAPI/Authentication/OidcAuthProvider.cs new file mode 100644 index 0000000..5361010 --- /dev/null +++ b/src/PSInfisicalAPI/Authentication/OidcAuthProvider.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using PSInfisicalAPI.Endpoints; +using PSInfisicalAPI.Errors; +using PSInfisicalAPI.Http; +using PSInfisicalAPI.Logging; +using PSInfisicalAPI.Security; + +namespace PSInfisicalAPI.Authentication +{ + public sealed class OidcAuthProvider : IInfisicalAuthProvider + { + private const string Component = "OidcAuthProvider"; + + public string Name { get { return "OidcAuth"; } } + + public InfisicalAuthenticationResult Authenticate(InfisicalAuthenticationRequest request, IInfisicalHttpClient httpClient, IInfisicalLogger logger) + { + if (request == null || string.IsNullOrEmpty(request.IdentityId)) + { + throw new InfisicalAuthenticationException("IdentityId is required for OIDC Auth."); + } + + if (request.Jwt == null || request.Jwt.Length == 0) + { + throw new InfisicalAuthenticationException("Jwt is required for OIDC Auth."); + } + + return IdentityLoginExecutor.Execute(InfisicalEndpointNames.OidcAuthLogin, Component, request, httpClient, logger, serializer => + { + return SecureStringUtility.UsePlainText(request.Jwt, plainJwt => + { + Dictionary bodyObject = new Dictionary + { + { "identityId", request.IdentityId }, + { "jwt", plainJwt ?? string.Empty } + }; + + return serializer.Serialize(bodyObject); + }); + }); + } + } +} diff --git a/src/PSInfisicalAPI/Cmdlets/ConnectInfisicalCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/ConnectInfisicalCmdlet.cs index f5e6bdd..08834df 100644 --- a/src/PSInfisicalAPI/Cmdlets/ConnectInfisicalCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/ConnectInfisicalCmdlet.cs @@ -15,6 +15,11 @@ namespace PSInfisicalAPI.Cmdlets { private const string ParameterSetUniversalAuth = "UniversalAuth"; private const string ParameterSetToken = "Token"; + private const string ParameterSetJwt = "JwtAuth"; + private const string ParameterSetOidc = "OidcAuth"; + private const string ParameterSetLdap = "LdapAuth"; + private const string ParameterSetAzure = "AzureAuth"; + private const string ParameterSetGcpIam = "GcpIamAuth"; private const string Component = "ConnectInfisicalCmdlet"; [Parameter] @@ -38,6 +43,25 @@ namespace PSInfisicalAPI.Cmdlets [Parameter(ParameterSetName = ParameterSetToken)] public SecureString AccessToken { get; set; } + [Parameter(Mandatory = true, ParameterSetName = ParameterSetJwt)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSetOidc)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSetAzure)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSetGcpIam)] + [Parameter(ParameterSetName = ParameterSetLdap)] + public string IdentityId { get; set; } + + [Parameter(Mandatory = true, ParameterSetName = ParameterSetJwt)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSetOidc)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSetAzure)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSetGcpIam)] + public SecureString Jwt { get; set; } + + [Parameter(Mandatory = true, ParameterSetName = ParameterSetLdap)] + public string Username { get; set; } + + [Parameter(Mandatory = true, ParameterSetName = ParameterSetLdap)] + public SecureString Password { get; set; } + [Parameter] public string SecretPath { get; set; } = "/"; @@ -58,28 +82,91 @@ namespace PSInfisicalAPI.Cmdlets InfisicalAuthenticationRequest request; InfisicalAuthType authType; - if (string.Equals(ParameterSetName, ParameterSetToken, StringComparison.Ordinal)) + switch (ParameterSetName) { - provider = new TokenAuthProvider(); - authType = InfisicalAuthType.Token; - request = new InfisicalAuthenticationRequest - { - BaseUri = BaseUri, - ApiVersion = ApiVersion, - PreSuppliedAccessToken = AccessToken - }; - } - else - { - provider = new UniversalAuthProvider(); - authType = InfisicalAuthType.UniversalAuth; - request = new InfisicalAuthenticationRequest - { - BaseUri = BaseUri, - ApiVersion = ApiVersion, - ClientId = ClientId, - ClientSecret = ClientSecret - }; + case ParameterSetToken: + provider = new TokenAuthProvider(); + authType = InfisicalAuthType.Token; + request = new InfisicalAuthenticationRequest + { + BaseUri = BaseUri, + ApiVersion = ApiVersion, + PreSuppliedAccessToken = AccessToken + }; + break; + + case ParameterSetJwt: + provider = new JwtAuthProvider(); + authType = InfisicalAuthType.Jwt; + request = new InfisicalAuthenticationRequest + { + BaseUri = BaseUri, + ApiVersion = ApiVersion, + IdentityId = IdentityId, + Jwt = Jwt + }; + break; + + case ParameterSetOidc: + provider = new OidcAuthProvider(); + authType = InfisicalAuthType.Oidc; + request = new InfisicalAuthenticationRequest + { + BaseUri = BaseUri, + ApiVersion = ApiVersion, + IdentityId = IdentityId, + Jwt = Jwt + }; + break; + + case ParameterSetLdap: + provider = new LdapAuthProvider(); + authType = InfisicalAuthType.Ldap; + request = new InfisicalAuthenticationRequest + { + BaseUri = BaseUri, + ApiVersion = ApiVersion, + IdentityId = IdentityId, + Username = Username, + Password = Password + }; + break; + + case ParameterSetAzure: + provider = new AzureAuthProvider(); + authType = InfisicalAuthType.Azure; + request = new InfisicalAuthenticationRequest + { + BaseUri = BaseUri, + ApiVersion = ApiVersion, + IdentityId = IdentityId, + Jwt = Jwt + }; + break; + + case ParameterSetGcpIam: + provider = new GcpIamAuthProvider(); + authType = InfisicalAuthType.GcpIam; + request = new InfisicalAuthenticationRequest + { + BaseUri = BaseUri, + ApiVersion = ApiVersion, + IdentityId = IdentityId, + Jwt = Jwt + }; + break; + + default: + provider = new UniversalAuthProvider(); + authType = InfisicalAuthType.UniversalAuth; + request = new InfisicalAuthenticationRequest + { + BaseUri = BaseUri, + ApiVersion = ApiVersion, + ClientId = ClientId, + ClientSecret = ClientSecret + }; + break; } InfisicalAuthenticationResult authResult = provider.Authenticate(request, HttpClient, Logger); @@ -123,6 +210,7 @@ namespace PSInfisicalAPI.Cmdlets private void ResolveMissingParametersFromEnvironment() { bool tokenSet = string.Equals(ParameterSetName, ParameterSetToken, StringComparison.Ordinal); + bool universalSet = string.Equals(ParameterSetName, ParameterSetUniversalAuth, StringComparison.Ordinal); bool needsScan = BaseUri == null || @@ -130,8 +218,8 @@ namespace PSInfisicalAPI.Cmdlets string.IsNullOrWhiteSpace(ProjectId) || string.IsNullOrWhiteSpace(Environment) || (tokenSet && (AccessToken == null || AccessToken.Length == 0)) || - (!tokenSet && string.IsNullOrWhiteSpace(ClientId)) || - (!tokenSet && (ClientSecret == null || ClientSecret.Length == 0)); + (universalSet && string.IsNullOrWhiteSpace(ClientId)) || + (universalSet && (ClientSecret == null || ClientSecret.Length == 0)); if (!needsScan) { @@ -161,7 +249,7 @@ namespace PSInfisicalAPI.Cmdlets { AccessToken = InfisicalEnvironmentResolver.ResolveSecureString("AccessToken", InfisicalEnvironmentResolver.AccessTokenPatterns, AccessToken, Logger); } - else + else if (universalSet) { ClientId = InfisicalEnvironmentResolver.ResolveString("ClientId", InfisicalEnvironmentResolver.ClientIdPatterns, ClientId, Logger); ClientSecret = InfisicalEnvironmentResolver.ResolveSecureString("ClientSecret", InfisicalEnvironmentResolver.ClientSecretPatterns, ClientSecret, Logger); @@ -199,7 +287,7 @@ namespace PSInfisicalAPI.Cmdlets { if (AccessToken == null || AccessToken.Length == 0) { missing.Add("AccessToken"); } } - else + else if (string.Equals(ParameterSetName, ParameterSetUniversalAuth, StringComparison.Ordinal)) { if (string.IsNullOrWhiteSpace(ClientId)) { missing.Add("ClientId"); } if (ClientSecret == null || ClientSecret.Length == 0) { missing.Add("ClientSecret"); } diff --git a/src/PSInfisicalAPI/Endpoints/InfisicalEndpointNames.cs b/src/PSInfisicalAPI/Endpoints/InfisicalEndpointNames.cs index 2d331ba..83a8d0e 100644 --- a/src/PSInfisicalAPI/Endpoints/InfisicalEndpointNames.cs +++ b/src/PSInfisicalAPI/Endpoints/InfisicalEndpointNames.cs @@ -7,8 +7,6 @@ namespace PSInfisicalAPI.Endpoints public const string JwtAuthLogin = "JwtAuthLogin"; public const string OidcAuthLogin = "OidcAuthLogin"; public const string LdapAuthLogin = "LdapAuthLogin"; - public const string KubernetesAuthLogin = "KubernetesAuthLogin"; - public const string AwsAuthLogin = "AwsAuthLogin"; public const string AzureAuthLogin = "AzureAuthLogin"; public const string GcpIamAuthLogin = "GcpIamAuthLogin"; diff --git a/src/PSInfisicalAPI/Endpoints/InfisicalEndpointRegistry.cs b/src/PSInfisicalAPI/Endpoints/InfisicalEndpointRegistry.cs index 1ccf72a..0478a5c 100644 --- a/src/PSInfisicalAPI/Endpoints/InfisicalEndpointRegistry.cs +++ b/src/PSInfisicalAPI/Endpoints/InfisicalEndpointRegistry.cs @@ -92,30 +92,6 @@ namespace PSInfisicalAPI.Endpoints ContainsSecretMaterialInResponse = true }); - Add(map, new InfisicalEndpointDefinition - { - Name = InfisicalEndpointNames.KubernetesAuthLogin, - Resource = "Authentication", - Version = "v1", - Method = "POST", - Template = "/api/v1/auth/kubernetes-auth/login", - RequiresAuthorization = false, - ContainsSecretMaterialInRequest = true, - ContainsSecretMaterialInResponse = true - }); - - Add(map, new InfisicalEndpointDefinition - { - Name = InfisicalEndpointNames.AwsAuthLogin, - Resource = "Authentication", - Version = "v1", - Method = "POST", - Template = "/api/v1/auth/aws-auth/login", - RequiresAuthorization = false, - ContainsSecretMaterialInRequest = true, - ContainsSecretMaterialInResponse = true - }); - Add(map, new InfisicalEndpointDefinition { Name = InfisicalEndpointNames.AzureAuthLogin, diff --git a/src/PSInfisicalAPI/Models/InfisicalAuthType.cs b/src/PSInfisicalAPI/Models/InfisicalAuthType.cs index 10e8496..d0729f6 100644 --- a/src/PSInfisicalAPI/Models/InfisicalAuthType.cs +++ b/src/PSInfisicalAPI/Models/InfisicalAuthType.cs @@ -3,6 +3,11 @@ namespace PSInfisicalAPI.Models public enum InfisicalAuthType { UniversalAuth, - Token + Token, + Jwt, + Oidc, + Ldap, + Azure, + GcpIam } } -- 2.52.0 From 09c3d5c68bbcf63334e103a3abaa0a67de9b5f6f Mon Sep 17 00:00:00 2001 From: GraceSolutions Date: Wed, 3 Jun 2026 17:38:45 -0400 Subject: [PATCH 09/12] M8: CHANGELOG + DesignSpec for 2026.06.03.2136 (CRUD + auth provider expansion); refresh published manifest/binary --- CHANGELOG.md | 13 ++++ Module/PSInfisicalAPI/PSInfisicalAPI.psd1 | 29 +++++++- Module/PSInfisicalAPI/bin/PSInfisicalAPI.dll | Bin 75264 -> 151552 bytes docs/DesignSpec.md | 68 ++++++++++++++++--- 4 files changed, 96 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7182481..dd1a5e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,19 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) loos ## Unreleased +## 2026.06.03.2136 + +- Build produced from commit d9822aab7a4a. +- **Resource CRUD expansion**: Added full Get/New/Update/Remove cmdlet families for Projects, Environments, Folders, and Tags (20 new cmdlets): + - Projects: `Get-InfisicalProjects`, `Get-InfisicalProject`, `New-InfisicalProject`, `Update-InfisicalProject`, `Remove-InfisicalProject`. + - Environments: `Get-InfisicalEnvironments`, `Get-InfisicalEnvironment`, `New-InfisicalEnvironment`, `Update-InfisicalEnvironment`, `Remove-InfisicalEnvironment`. + - Folders: `Get-InfisicalFolders`, `Get-InfisicalFolder`, `New-InfisicalFolder`, `Update-InfisicalFolder`, `Remove-InfisicalFolder`. + - Tags: `Get-InfisicalTags`, `Get-InfisicalTag`, `New-InfisicalTag`, `Update-InfisicalTag`, `Remove-InfisicalTag`. +- **Secret mutation cmdlets**: Added `New-InfisicalSecret`, `Update-InfisicalSecret`, and `Remove-InfisicalSecret`; extended `InfisicalSecretsClient` with corresponding create/update/delete operations. +- **Additional auth providers**: `Connect-Infisical` now supports JWT (`-Jwt -IdentityId`), OIDC (`-Jwt -IdentityId`), LDAP (`-Username -Password`), Azure (`-Jwt -IdentityId`), and GCP IAM (`-Jwt -IdentityId`) via dedicated parameter sets. Common identity-login flow is centralized in `IdentityLoginExecutor`. +- Endpoint registry expanded with login routes (`/api/v1/auth/{jwt|oidc|ldap|azure|gcp}-auth/login`) and CRUD routes for projects (v2), environments, folders, tags, and secret mutations. +- Test suite expanded to 139 passing tests, including mapper round-trips for projects/environments/folders/tags, secret mutation DTO shapes, and request-body validation for each new auth provider. + ## 2026.06.03.0131 - Build produced from commit 7be0b7b42008. diff --git a/Module/PSInfisicalAPI/PSInfisicalAPI.psd1 b/Module/PSInfisicalAPI/PSInfisicalAPI.psd1 index cd3acdd..3e80f15 100644 --- a/Module/PSInfisicalAPI/PSInfisicalAPI.psd1 +++ b/Module/PSInfisicalAPI/PSInfisicalAPI.psd1 @@ -1,6 +1,6 @@ @{ RootModule = 'PSInfisicalAPI.psm1' - ModuleVersion = '2026.06.03.0131' + ModuleVersion = '2026.06.03.2136' GUID = 'b8a2f3d4-7c51-4d2f-9e6a-1f0c8b3d4e51' Author = 'Grace Solutions' CompanyName = 'Grace Solutions' @@ -14,8 +14,31 @@ 'Disconnect-Infisical', 'Get-InfisicalSecrets', 'Get-InfisicalSecret', + 'New-InfisicalSecret', + 'Update-InfisicalSecret', + 'Remove-InfisicalSecret', 'ConvertTo-InfisicalSecretDictionary', - 'Export-InfisicalSecrets' + 'Export-InfisicalSecrets', + 'Get-InfisicalProjects', + 'Get-InfisicalProject', + 'New-InfisicalProject', + 'Update-InfisicalProject', + 'Remove-InfisicalProject', + 'Get-InfisicalEnvironments', + 'Get-InfisicalEnvironment', + 'New-InfisicalEnvironment', + 'Update-InfisicalEnvironment', + 'Remove-InfisicalEnvironment', + 'Get-InfisicalFolders', + 'Get-InfisicalFolder', + 'New-InfisicalFolder', + 'Update-InfisicalFolder', + 'Remove-InfisicalFolder', + 'Get-InfisicalTags', + 'Get-InfisicalTag', + 'New-InfisicalTag', + 'Update-InfisicalTag', + 'Remove-InfisicalTag' ) AliasesToExport = @() VariablesToExport = @() @@ -27,7 +50,7 @@ LicenseUri = 'https://www.gnu.org/licenses/agpl-3.0.html' ProjectUri = 'https://prod.git.gracesolution.info/gsadmin/PSInfisicalAPI' ReleaseNotes = 'See CHANGELOG.md in the project repository for release history.' - CommitHash = '7be0b7b42008' + CommitHash = 'd9822aab7a4a' } } } \ No newline at end of file diff --git a/Module/PSInfisicalAPI/bin/PSInfisicalAPI.dll b/Module/PSInfisicalAPI/bin/PSInfisicalAPI.dll index 0ba2ef8e5d3436364f7cb6aa330abc4ca7ee9046..72da4ed07bb9ab1b8de515b598b68715f5e23e56 100644 GIT binary patch literal 151552 zcmd3P2bdH^8g@<3Oi$PVvpEoUfrViMS&~W)5)>6dRLm$w8hgY;*2VSA@#M@odwQn3 z^AvOD)HCPIi8<@(nLYU5_p7Rz-oT2d|Ns1}RKMR_)fK+_Ds)wK&(=GiuEI*G2>xx@ zpww-+@^6B;T|2GtF6@0rq56B`iQ3yjv!AHlVZZ$|HA{VOvA^fRHGA*5WQliZ&0hP| z_{)~m?7yUD%AC112YZY58B|b^>}R6hcB)ddLt*vxgL}MYQ+reOsmTisQEDqkDOa;! zE(WdvZpBrp?t%fCZ~obGydKnByY&06m(|W zfD@FOFv#EMAP)hVSA{$)2HB=VxJmR5{yj^*;*WWAR8%!SrqrPqgj7h_U+(h|NP@8VcfGZ{S2zxTK+b z!mCEYbxpDU39l!Rn;ro-n8f!siQ>rGivB^LxgjL}A<|f1l|p$#Vw_=Hovws_nz|*tmvUmGlcSY)d0k!;!ddcPsNpG~OG7VI2)a%bnVZ=+zHR>T3xr#wi8VZ7X z{T?w!{mG76nyx~D+SD&>e5p?-R+5nVEsp34PDOMLqf`yWQAn)gRFsuml!|&aOt~rE zAFvOgn;s4~m?(@&TT-#xu9_dEkEv!CIoI`kqB^ZSB=t-?9}j)P0DlX8#=;xbXPw^! z&!}z&nPDsvl!~^3Yt$PoSJxYgYu4(KskX`;!x%QA+@s;=)A_BHdk*51YV4rgb@a(8 zSL^m{by~liZeKwgivGXVZO|~PBO6!PKJfGD*xp90E_nCh@RX9bGpdKl>vF@Sj7=S= z=G|xe4oB62$B>zFnw*k)Cs~%)P~^tW&yP!o(B#DHW919-VlJBX2PO5roMz}KQs2i3 zw+H}%n>8T-1o~J_2mpa$O%nn@BrG8SMA8xhK;&6M00<0IIt~FK3M?U@mmWo!!9wdH z07N%S2nd8k_o?F)fRHIm2nd8MvK|5uve*&=Kwu2haR~_Ait$W)2tY`TJ(>^z0z;1` z1lU`PhlJHts2eEJ)QyM4&Cpfhcu23>oNpM z??oK|+H}2_HHN-i?^TVV5!ZX2*!9+G3fXnjn~{H06Bs59@fHZ$SM?A_&BPTcAkS_Z zCel!!>4Dxg>2Taj<~5YZyj}od=^69mjWHMFB#0RM&ScZNwaHkc8}n)*yza6SO(J5p zS4WkpzGD;$p%lkF44?|;lXCRPl`IOaFGYIQv3e)n7q|7^wn(M>0U(J?9plM`;QbRA z5&pE{I2jBc$+~iH056fIa9KmBoC8U+uEHCLOAAUH2{(3knh7$&@z6MY=c5{hGS57m+^a1$O3C8;L_-blb< z@3`J5T;2L%a$}uH(4yGPwz#4h!)EgQ!%-$Z3FK_@iH?f6&2-zO=eX~F` zHZ&o?c3$v6-D(d3AkbPgApis#h$aMpKoik~fIwVmHrhh~LSho42?2qSuzc+y03p!| zG$9}m5=}yT2tY{KvnB+9fGKN2K;Tx`w)PMZ@PMUj4*`e*Ca(zrAYjCr5C8(^tO)@i zVAz@v00I`S2>~Er7gdA@P0U#z=LI8+~mJk49k|hLym~06F zAf{MC0EnrU5CCGDB?JUAINf>(K*+5uAppb-O9%il(-Hz~T)J&nhLpk}Z^z3y7@(R8 z9B*q5R5!pupPtP8Lyun2fa_hRG0N(CS7?kOPt8#xl=Sw{dSTs`Uwt7ZE?D@xH{*XuKcsbHJC&{8>jj>}I!~X)j-5v{65j_G3EU0~zll8Xrvj&p;ZydDvqv$0=B(Zu$a1%(jF85L;V9 z00>x!zC{2Cv>#0f0I{tl1b~1w>ktA!Y;OqxAYkJ~F`a&!m*AQoCe00=ZC9YO$z-7Fyh1lpDkAs~?B zJ*}?4FAYf2Bga8oxSV8~@n3)bC0K{TT2mk@&(;)XLfbc9K00i2azC{3tLo6Wxgl`D}ATpK^ z0OC+f2nZBmne`BWkcU}90EpjNLI8;6mJk5qa7zdPvBDApY-%5whlOxmQ-1%za%IaA z2p(LsY@uw|ELSov^tEw?H6Bhx+pXVl-ubWWJU6>)>HQteJFs+FhlZZa%tRBWyO((%0w!1esMJB{}-{OqN2-G zqVAnQVG&ZDEnSzN{F-nSL5pPBYEQAAr03$#C?FW!S$3IO5%bg#9M~OJXJI z-l*x~xYJD2OPNLKJPa_!2}rVJDeKa)RKi1(bkjG&2}b{^jlLL9uvm@GB|J5rT~SKu zvAzeN%|8o`8Q>IRSi|2)XZekeS+k6*D`4}bKV$O|?=j}04CThE{HaKy&8qy}AaoE} zlT79e{0ZFFu8HJH6UmQD6N!mRyp71I+-ceQV2dL~5n07Z?+7W_$X>y{#nGau zZe>kzUmJ9m?%JYXYk#+HN?E$=yWePkw{GECx~sd}SnrS73T?kc9QNlVSlQ1ZDS>X> zSTpQXtl8BI@cFdnSjyGb9OD9fEEV(65xX*14chF%h*GZ$$J<%+gY+@$!g0H|qMQ>> z$k}fZ>=%aFNuQla+Lelk_a@WrV80RXE%4~xD;ZQansT=L9=zJwZoslXu)aCF%*&?G zgv}xyUCbsrE1QDZ%F>O7`I1JHpZNnIwk)Eq*9w%1#=PIlH8%`=1X*&_(Z;=FBy7a{ z1XoFu_DvyZ-`;U16pQVhV1#!loMePAD4b%1FDaZR0=8yYy$P>?6^q@LT$j^=qG=6x zw3a8t?=wW<6UIwY5#9Oy3UhfM5p`}Zo9DGL7qgzeBy{4pb{`?G@?kIKKWi^vBgeMt za{XP-IQ|R&jT^@X>-+1BgPAbKaXFwij#WT3jx*$%Gma33l#Ln3SrRtlg*m}sE1+>u zXk#4bm{8y2>YZnVA1Pd5gl{QaWP}*+zr+X$3YUqHwUojRmU1NOC*rYK9WCW4@rxoJ zpYS)aln-eS?;^_L_8rTZ{Gg@i{igjwC;mHUCl`j4s`yzu$w%Z}*-0V%H*P20t?#e1 z6K2BL$#u}Jwv#KRGGgA0Kb{KEI?mQdS%tokT_w*l1n|k776Ml_9y5TNo{L)3sRS zFOE53h>=vRFqZC%l#An-`<23{<`GQNhV`>jBjOocX0nR`^(O@Jc15JAczr6Ka?^cy zb9UkwEnlFEus%|p)Wc8N`Vjt4C3kOJno6W=5xjA6pabt|OZruyq=`b7y3(Y_V%7>% zjVURnjBasWQC@Baba`D5e-8ac4c-SxB9*{|Je*3bNFhfwNu8_lAFki9A%){3W&QoL za7RScC$sF#Ul8vK?y>v?1yrF9GrOUf*o%@bYYWH2^E{Tkdd9px0P^Faim+L=;zre; zpv^|rYi(A(>LWUiW*bLu9S3Tg>Wr8;64)4F9BWHVEUyG(vG>;Kd-+fiyt^sxf(T^c z<>1XO)~$5*yPMmi=*EM=KWG~~G=lT?QZ;a#UZZo=PO9M;i_cPx_gnnD>hXof+&{tC zt*W6r$Ga9KGiPa<`nvwd&~`P~#*$^jN?iZPoVY#$^JoZ_>15KTi>Pt^yl&a32X{kCTuU@5$zmec)J{dd@F+g+H+%l*u!BAP9gsQpUlmO zbcLp{J@pK?r?4uNOpimCnJHoFQ7_C(L}xClv1BerjDs8rUfpJ<>E#I*^$K*j2$J$r z8oR{Bz&HtoS#>1l;lPYrzq7IQ0w)~FJ(r-M+#9SzIbGVurko}C*@IuLNHx2Naufw~ z@ay-$*msSo>ychBq}TS`7alcutN$St8trbnd8e7Th5bW7h*M&YZY7;&MsA)fx?{A- z(@zz##(2z|jY2mS%JW5G%-aT11GcBhruT;vOzLEtRK5%;xjcMI5B-#(_awPXar7Vc z84Q-=S(Kx9hjM65Z|s;_L_Z4$GcZ2I{%$f35(Q5*>0+M+Kc9|kij8Vzv18g=Qz&+R zOa{rgoLGx{+o5opeS3~1nff3eTJ)JZ_AYzlRj0m z@$$|?3}yjf4`w3b?Z`{i+ld#L@Gf$Vdb`Ti^>)WKTa87m!Jt}>M|2xg%Pa8n>6D;L zHV65r7PDqTwX{3PM`h@Pd{j^ewS0}Z^psGa!@W(~A>Qs&?ag z$vyT3jw>2F$Sh{QqTXJ-Kt+q>8uj+YHCxqKOlrWHtz4c~Ax|4q&D-$v>9{3vQ4MBf zR81mhF~tyMH36#E=*Tdm~RR!%K0L1B*5C8%Xlsbffz^#9@9s&>&kDNNBfI!GIt%m@F!~>@e zDIgH?Z0jKaA@R_uLka+Kt|bJ3z>}yBAppermJkq#9FM6wqyU7x&=LYb;MrA&5MXbW zIlx`83I560n7M?_)A1T{(T{i7gd7Bc=d(z9C+h%1h)>lRr)gd94;qgkUZwF!T(Xgj zhQLTjdS~bWV~H{8Vv1W3pQG_4T+#=SI8PJfNE}Gw0!>WF5;zy^%2}rwC_`VgL7wj_ zL4Q)8*%vbq>=$G6N$(f$#~BHS3$tI0JtsSBZns~20DNp);rTARU(9`J_^EzOhx@Y! zA|TQ$WqMl590Urf9*j#~L%*U>dI=Cxtc-gzm}{BS=VQ`N$@AceFiUZ1dJZe=q4i~$ zkeQWr*E<9}GNR85^;1LVDJ(8T!s9INM6@GbUs0U&O*ga8n$Eg=BJZI%!K;&w|2 z0C9&U1c12H5&}TL;B~nKAlF9(b4>xcUXji9@^-nt+ukk!@!ewy0U++Rga8otSwaAa z`z;{=!~>QP0OCPQ2mtYrB?N$2V+jEu{$U9LDA#Gha#29JPRW+*q;}q!8JM=c=$#6K+|0K{XK5CGzFO9%k*ge3%kc+wIAKs;p$0U(~Xga8oFSV8~@OjC7E z1%P({DH?rP+p3g%*nsQw$ebVLZF3=FJZ&w8`+av`b53g84K%nWoYCQxXN3RnF>b|k0=-@D zcL<6=d_K2f$~PfQMf)(zJH}JlKFr}A!&G$|rfrcz+~?6n`hrC&9)f*ml&Am3=fLGz zEkx2OJi;gS!XOp`3!}0wn1}azqA@HI{sd)UjVrez*v$xyNOmv|{>R<)e-TD+7_Xq7 zV7Y1l($2ilejQ^f+MC-0|PIjgs?$bE^NJCT;JXd_%t*w0ZaZEPgBOBwd!Sk5U)uRwiZRbdt_m#z1u|yw)A_2sMIM`c+E(G4gFElddc=ZSd#Wm z9|7M((egq#eeE#@opON|ycT$)hxr)ydgzprt~Qq=1%5@gmC$Ksace#ZE48zCR0rnw zWXipxaY-KoCok5BQx?Ypag#EMHJsyl342Lq04t$@*t#=$*)@Fvd^O{p2v`8`!mM{( z3&^Fc*PXnI5(0~xqPQulZ|bZbQsug{_5OfpSr0vhz!aFRy4b~8lD_aUi=I{f z*7VNRN4z+;cA{(xoQ_b-B=tlp(O444GCp&FwfI%=VYo_vJKT&t1BBA=F{I*|)d;g9 z6{`=y*c{aoDm*!qkER2BYbbf2#!6iqYVPMu9E7rQ?fn|~4;0%BVR7@1h%Gr96$(w9 z0i1_P`gTk~I*kL3l#lE7V6)>*L0q~SyR7acJeo(t=5fCTxeU;VIoZ%J?z7to(5x}M zu;!hKYdqndg^TOez#Y46!-frOlP+E&&}ujD5ag*pUZmH+tMN?0GQfaSPS`seH+f~y z1MoS-6~ws}Uatq_uy-!K&V$1jtZMjzmE%>z2bM&yxOYAS>DE zm}BNbh}as38C}HCRk(j`-=JYRD|gO%BomF;Z4i5_5A|T3EK}W%(XabDY9BS)lm~N$ z|FGZSD_sIWd~FE>W320J z$9V=!OfOGz-TBd+=T%q0-&>by&F!SOqA-eqK>=|04l1AAA% zfkE-P7fn$$Fuf*MqN&eZ3D0CXHc!}C@*8%EOH1JaqFhUv28k`Cam!d!!tLGPy9z>o z*M_+olJ_T}dT*KY;~nXoUs+yU8bQ-VYm@Ot4E$m}uFTs^JE%~LkF@O|tR6vL8<|&W zu8k(c;YL_Bnn<##uhYBPyB4TySeaj1p_V%SW^nax+=D3n3J0$o>RuqPVWu29Nap*i z5C-Q6SvvnL8y+teRhd1XT6nhU4x|jl5f{hvyg%bEyWDVNn=P*nc{$IeO2*du17>nRnwv2yPQAbIzzJ{Yp8 zIvBF4K>Ni+JN0f%-|(}$BsgY~+a-xmFuNo;>cB}b=L7RXitnhk+b7AMHj$xRzaQT~ zleHdtPg|cIphuqFQbC6Oohrf7DM{#;@_CGi8i7^FYxxyuS=cEKzO>EZ6%UUt z)V&)n!T1kBOnRwW9}&o<{0x2cN^eT``1D%u{p#?s_MAXe&4=9#hR)3d7%Hi)vi%00 zZ=!{fbbqAC@c@;L7j8YSfE3!->$}H2u8ml^G58|x)As%PC8*aSzh%AZ8vmc`6=f=; z(dYTQXUm$Ii&Ek(Qhb7fZz9g-1^W}*@EZ5F#Rav>TEe~p>xVDeXY2TAL{r#tsb1Ij zbMW-(l;G5teoH@UDmI&Q8#&l6KvQ&mHM%ev_7wAC+YW4XFG7P;3wT+S8)^;0fw@vjJkYC#i8 z7GxH(a$Ij7FEMXNUhwAsPQ1pwU2qAstZHdgK|MYwQFYjd)Rk7;1C>T6QG$Bmj$1J* zL0YNVft5<@c(QXNmDYr)bfxJdSh-4Tp^s^$*|VMBsN;^v)fx5^)v({P&VIA@ldChc z)z_we{>SdKZQHsV`q^mPD(H!7qJtq-ccx|^ET`WA@Dk5ZAmWw~03u-t0U(l=5C9_25&}TvTS5Sc0!s)0QD_MP zAi7yX00^A8(-{*Gn8K#4hk$?w&dcdL1Oz;at%m@_je~MJqyP}5mJk2}$KG@Z0U*jP zAs`TWh4m1Ckd>AY0HVqg0zgz-LV%4+*3TZab0k@t^~R!y&;tR!Q?Nx=(BCPb%nd3A z!v4+ZoOgsY<-E*^=oh#>mi6TJ7zEiK!__-n2f%#U^_n#vM%<$D2;w7&W8P7`xZcs) zXB>ReyVLtvO-#rV$AjQ|I$<>nx-{7OqmUEA*-&i}h{B?UvT#tJZ`5b|KW?tfYoXorTMUL=PaxWTT z=00F7?i`@0uiH^+e51v6<>{gYoogiQ@!tHhf`;ugFC#v#V7M(?!6y}oC(dt~E~uH8 z#>P}cfOkD~rk~X-4VA(|{3~|J&U+C4iSh*niTG*z54$GpEkzRCr7FwZOjYX4@lB_s z+ssVJvqmg^3ssgiF|o0uP}a>@)>k)PRy=4Qgale4rB}Ya@3B<7yq28QM&jS$G<}SE5SbuW~!lA zW*Kr-pIIU9p5p#a+`Z}gqk5^O-PHOrEUoHAa+&(;>O;fpVH=(R5Irp+07NfK2msOB z5&}TfSV91ZT1yDXsakaX7;-(5x_)Ehy3X1azUR@#)Qnx-iYjcuY|m7LygxN5vzE2M zmk?So0gT|#CSIs&*;-&TFO8L{sDM;-z5A~=RS^rby&u?U;ex^0v+?m$ew~u7zHe?iECApm!m+yEb>&m=4aS1k^ii*r#pzY7fKRa^JJg*A>=Wv!?%%#pX>9Yy*F-~+oMozb?}bSf!ne}KnHk!fj*jf5gllG zz58w#I>0@Wac3_={0a-d{Hn7Lv3rhSOaHoEgfP!VJuhgQT<=F zU33UI<*nxq0zmY&ga8ozEFl0yoh1Z-=x+%DAO=`M0Eo0D1b`T52>~GLEg=9zgCzuj z!2XoZsQ?g7mJk49kR=3I>(T3Z95ct#e$IlTF~>N9m$0{z7dgh}%D2vvUTc=gi=-b$ zZSg(Or1$$QjaT#@1yk1)>0iPZy-JsPGs2}M2qQ01(xP+BYPS{#JxL+PbbENn`faHp%2;| zWpov8Al$JCm&CE_dAPN4h>7wE32MFO!;35a*-eUIeopL=&R24?@PTwr?JS+r8kW_1 zb(zi|I=jEG+;6?QxF3765jm^01@)aikC8cCXuy)P7|lP$MBM)V7(|&DrWcMkP^tM& zT~l6ve{b-aOCaM7gA7LAWI?#GSJ=y`IfXqrN}0#u={*65=W03kI2yLRCDUDo-T{@} zyGOmVY#R-$0ckId4oR$~0+HKRd*k%ISShwTCgIyBh`t8L$<%PhVPu>hqg-TVIj0}) zU0~&JwSQ@SPkhe@=6O-z8$FkQVVSOz7O&~tI=+(Z6_by~N{ViO4ODUV=WJIpljr2j}oX-vP`4BTb zd~X#8k@Z^NY<0@Z`W!FY`$^Ep_+O=u>~~FP;bl|3ILGg;{sXxTd~fw2RWyk4xgU@gS@ zbh-wmjbKlB`gDTd38fX-_59w5u9Nlr*N~)}{swL^sbdk5u5i7apR4e<>0>Hf?*S=w zkvgU}w@!#2L{0o{^g6WfMs--{FNUZ6?&{Z~z=-#ST%+EXa&^7`;F?uvO?!nN%@{f? zy=r~F0Y9J4Z=%mozm4g07y7i*=h^D$%A7u5LHohe@~`Mmvh8P6^%*pUtB}EsDQN@z zd^)!0G3%Z=bLm@W3NGuOIsPc}y4dVDb-(fJzO{<;*m{5xfYD;SB?N$&U}>_@chNqm*%O~LqNb|n)MKXkkc(8AP{mZ>mdLkXIMf&AmmKz zApjv~SwaAa*_IFxxOHpmApjw_v4j8+b1Wgi-YRPXoI}N6i~5~4??p7P?AqnaSwXK| zQsxpQ10^%RLwAe^h{v;mut@o&2|?03SO<8T*wgq~;zKlko;X8{Ep}Y=_t<74Ka(Us zc#jv&yFT*_ssoD^&*CEAPkjy`Ed85VdVC&4=ig6#0X{HB+G6ngsnE!_RwDvHY-b4p zAhx%J01$I6Appb~ zjdn4EZep~@ZhEx$|AJ^=+w^FQ2W)z72Q%!ZDPjL#5bd#>9__uqAlf%JJ=)6jrYm6# z!*;C%v`yOE_;&WjFFIMn;BuX|w?i18wl_H(sqO7|*;LA{y>&3O&f`0$)A%mhg!uT4 zo=wf)n*TYz&hxin6XN5iU^?b65MQ9Zb`syl+AF^s(q*(02O@y3mR}Gpzf;j=v|D!> zZML@cH#L`FuF5~#$?$z=6Zq&5iNFpCPx9?cbOypZi!J@=Q#@KzRMi(_dL3cD4_dJBjVd} zrI<~Y$rpgI4jty+rk?NWCzxosWV^C@nLImY?%SB>HpX532_o$sHW6%B&Q(oDsGma) zKwMKWch+-diNwQmd(wLqPF}+(EZe_E@pU-)4c%}g_YEL^`k(V?h--0a`T+0RI_t}t zvhUjBwtvy%KK&AIW7dOv7y6ufo$4`BzkkbTmMJ@H%<@fWTfRJhdZS%U*=Q+K{_zOp z=q>dJF-6BOFW^B02T1gzwEz$}ZK(+XAet>90K^fN5CEdZ5&}T1w1fZcVyk&cZ#O6XCh9P=2u=%gW0nl${CYp6KNWVf@;G?l zbgPRRUx$DzkB6KuCu7|7TOhK(&k$GLE9{waM`15WP3z=-rzx|@3RHSa`pZ7h1e7_3GTX3WM7sl`z`Ikca6=k<=EAZkyoLehn7)fPj%_72b#vjF{PgC?m7I|< zD-zGNISgg%tU?W+rFC_=%`{#Gke><(4?$TSe30xDMCU)u{BySt-Ge+U&Bm2~**8w# zM+n%szd^1Qe&ry_mHu$KR{3+}TJ0~EYY+b@x%Tug;xBp=?3sGZEdRK8jg{FB7#J>*O(RQ;lq}5g$+X~f#csNVobQ_6R zvIGpB#K&2p51vRsY{(K=VN#<{fs^?s&v2CIXKu!|&Pk7_?Od+$j>K1LJd60xz1g3;Fq!7Waa-FMFFFDh*4DiqA0dwF8-o(G?(~q zlB0W|vdsC6uKE{6mn&4m%FIQ)zsLW2|G3N(yua*c@1M?WUyZP+uNlPG0Oz^*5XCEy zob=1&E(M1d*YIlH-+^!*v2-x{-kUl4I_b|~W8UX%+{?gvU(nXAZO8<>}@re?6dWhPe!@?+u@|*=~oeO zE#4NKh=Zi|Z(FoI{wo84rCI!jV^qz+8qiEM6erC#DD6-GOh?i%k0L(J;AjC`ihd!j z33D5&AdOAhWYgYGnmW+Jd?!sMXkiAvukNJTk{ZY9tM?yfeQ2z)z)7=Kb-3DCBgTH# zEPm`t*D%BJ3x2Wkh6sNbi^G=Gvo7enti|`RfE~IFjV2lKy zwt~^j6gg+-^AS|+ylJ3MymE8g7Ssy&tF>NWNs;vYcK2u73GOFei9^(F@26pkUd0j@ z;C?+`hE)n()hyUm<{Ri5wv@9RJPSx2$;WX5Mns-9Vfd^)k!J-MK5JU!sdvM-GvCHO z#_C{Y(vQ=cnijZH4RTAvtPU35I67dGjGwh(;uq6IWW0(AH^HBkA^xuSEvBZ+ZxXHGzfN5$}7ZXz7_R0y4N z2u?#`TNw#BpqtLtkB~##JVKhiu5z{HSpD2M)5(lS3N6fHu5PceH79`O%Aj00FO^D< zK<1qE{z2tW&xDa+Ud)T&;uj28OUq7ahCVV;$es(wc-6s6Mw3%2+jyI5SzD5ib8rab zx4_Ab4OY*)?WXmN2XSkJ*Vv2-fX!WN2>~GfYzYA%uCs&y5U2+|w-o^521^KNZ?^}t zcG$%ew6)tCapOkp7Imw(6}$CtTa|ngxUO-ilRkve4fM}IiJdh4aN;j;)?GeIpx+3- zkMo1Z9{Lt}E^6%Nq?h3q{;UX_LH~@;O*VWI3mNeygYmGk!+8TE_V(A9DS8KLoFP6~ z<6umlr@7z3Ki$kghk!u;$cWPW0K48IxNGqPV*V4Xve<~u3m{J?9ox(^&!{H0nR^^` zl-z!mpR1H#7H=rguRqk6%UcljnHVgn-f-4lS;Cwd++=G^0IKjWmJoo*X~XE})poSu z;V1;klADob*04lQ`bc;<=@ypvVK6wO8VlNHIm+mBvALsdjvU#+Hvek#Ai(5dKCXBW z>>>|GGj)5?sT&X6V*Gl}QNMw<@;93w z0Z`J-mJk5q@0Jh%;ucE?sK)noc0~T?cAoz_t-}*VhbN1Bs<_9qL<2g}=PH(f3&i=<# zFC%_Z<12`t*7z#oXEpv4@$(vAOZ=k7*8$^;6tdE*-(ljZ3Z`%aeB|eB*nel@95Ath zOrM)N`d~4cK7Z}#vlc#h?g{j0m9(IkSB(qL!Puz`K8^4Ad$Jz6OO0u<={amYu3M+; zai)__ATupgVy>Tk$ZAdiRJq0y0zmx35&}RxYzYA%9Js9A0;0LwGnhqXow8z8nNIwq8dsq{X665WP&g;AibuKTI zmGJ#BRJeKi4!$DxF2)c%rJX@b;Ll>=hYYe~Nbp%4W3S@BGPDnlCwzx%8SqZfqpX4I ziS8m;FP1=)FqyoD@i8wC;41WQA!;fgLSsD_M2X}bM&JTq9ztdaV&Q{Jqqnb3#g<|H zIfSJW%#Lf3#Ow0hMq738D>Ae8;?G-RUBK8XjUi{NPgx5P0F!vy5(03aofGVwwTU(@ z2&HBp{4{ipv-#9GW-1+e;)kh0Zp7t!V%wHFnG-x?^B}uu$dN0nyWtvpRc4}v7?{ddoRkYXAqLwOP?XD zL*5r6@Aza#`fLoGm{xI#%T1pHA?l{j*Df0C;b*&?-Ay< z-K1|9#3<*ivlVRXcgG#ujkbWbmywl;b0Dd7qR11g@9vq)ZR9Ox$qalrp_;GbjK#e!~(1K&-Wd01*GOga8n4 zT0(%-T}+*b>#n+-Um&Oc_knU|Y~6haAH83Tt*TxSaKbcAvh8qZCQOXSNq;Hsf5iO; zZp<6lZW!bfeZwfW+@jzUeXgw>dT58bU1!T60EK$X5&}THZ3zJ&-m!!L5bs(-0EqW2 zAppetmJk5q14{@1@u4LIfcVG~0ziCh2?5g2jYV&^o7VMD(1^b~3a{Hq^o?5hIcaQ~ zxlLIAT@UYudM&Tq$*z(t09fyS!c`I<9dTVNedH6eNvWy43>L09IbrPHI%3y*TT>41 z^4`@rLW~ZIL0w{Oz7fZXv5`ic56n4HaJqzLGkPT%42$E$g$%~u`s388DIY%vSz3fn zJp~IX*W)TY7_d8U!tx(p3}`8>P zaWU|m^@-KK0941PmJk5qGfM~n@wp`g*gk;c68eA=WZ%C(TVI{`Ysd-*t15)mcVOR8 za0LsG{m>5m+Q%#+4}NS)AMl0EpaA6f-H84(%92 zogR7wAnX6Jga8mj~x1E1j@U(lk%E=JB_e7P9QGt zbJC4)y?QD8Aiy?dXNX+cA7t5iPN}o9@3&uvvVUjuEdT}i-Vy@zoan$@IffxM|HnW% z=55-{ez!A`DJ`%GY{a^SW@TqPI`Wxa55(o+zI-#E;+2_~;K zEQ>?uP{lktn|oBxwu!&|rNgs2K4+>+F97OrEFl0y*b)NTx9_cxy8l#GKb^O4(?`uj zSSQUpJQw|vlb#LO#>VNFvzG5!6rKmmSxcAgJ7RMx09lV(LI4QY5&}TPEFl0y+!6v% z*L=?t1D~C1Zi^iH4P&i6IBOSn(k@JYwmrf+Y2M+acMx~JxC`KVk9IOU+XwN=n`nUo z+xD^Rt{vJ(!sb-~GM}`B01$bW5C9_I5&}RJSVBOcyk;J{5P9|g^fTo({n;K;y1m5Z zeNK8eaTme$26j^3msnn&+T67A7TUZDK<2wyLI8;FmJk2}KiZ|uTYw#3x%P(9WIyD~ z@3wiaE<1Z}Fnt`uI_U$%JxJUoaJ_mq)^^Vg6A`2QKv1?+whlXaZYZ)D6o4ETTS5Sc z5=#gGQECYRAj&Ku07SVZ1c0cpga8ngmJk4<$`S%VR9iv-h#r;@0HUWQ1n`-98XEeZ zTDLy5<2Pq@8-1e*eop!@(41=?iYw=ulvnP8h-3qAXMpwY=itm-lRjm;S zeST=YzY#ZP^Rdz!eXi~C2oUKC;4x0r%0P^*xa&zVq{M1kGS*Po^&h;_^lbrl|E*a+oWZx4oIS{~!eO zw!7qwE>q;SP2T>UP0@JTyh+Gl-X?EpOX2giEB|=N`X0&K*PZ8$eILd&g#|mD8p`u? z-@lJSLd}_9R4Al)1CuC4kyaF90nWV$b+lTXC!tys+7lG=C@2!(#r_4?tg0Df#e)>w$2SpN=+(1w-@`}ZR* zeY9E*cUkYvp`7A)QDS{5nn5+#A8=FI(4IvJ*&XMPyBhMKbP3H1t8B?sti<~s;gH3? zM`%dbdl6gtr>k;{3JvtHN!=ZbL?DU^`h$>9eCHsI0(BHB10>Mw%{`xn7+u&j>Eqbq=W$}mKkMx7+(#GLDe zK;qoBt4=_?iy3btIvXrC;SAHSi#szMk>ICHnF7D+n2O;z<_K`!W^nXJ8RfwBu=my` zOY+?kQTgjJZxp=on+~!EF<;djX1`Y%RZl=O{Hr{b#i%PUzqqcV;^J1?PSvS6ycnu0 z^HUSl(i&(@Ww7pqAEhYwC$?iM{N{E{rGG_Rrj^GA%Wag8w)5l(!K`PuiEYwr9SP4$ zd*`lgJ&~EK+A$UWr)`{s*yJ`5w2ld8ucfUg zg0F7JAbV|@Rvui;X0Looo7fS-oVHBs<_MnE)rsvqw}2-GbDr+T`$Kx3u^((O6Nk-k zEZ-ji+-ABQ%Xw`xmfw%~>^ws}?O2`_>^viHstxJ$^>lwaWF4W5q;SjovD!S#20kt2YT5oCEEX4e9g!d;bc^`X0$!YGZlh@%{QI zV>NpW$aZT2^H5@~%P_Y=vz;J*N~#>HSG({L}!?TmAPz@r|7cBAZ4;7o51yhGS4C9*2xH&jW`+3 zLPRq4=OeK>u~*cW{(dq0X{UKB#2_Em#Wz|2Un=teh8Db()E6po{V`OOr!h}S zn-G7)q}8z7W$#NL%6Tcn;%hI=z-%};`rla&Xu}b;Cv5m&x0OH0VTiLitXU7iy-l^2pbQ`Qkh&x==%_Q%vn->S((Q$kI=`54~Yl9 zO}i+Vp_W`kxibC%q`sp=4$l#xlY9b%EiyeE=i^s`Z1zOg%%8`2 zzcKmSQ|E6F$zS$C8~OY8m*IB=^&2(U$h z$3E6W0Em4pAppc;O9%k5pCtt3;?gS^+9s)|rPNbW#xydHgrc7PxiRoqaWHM;&>=5R!(ByB4&fme3Kbi7yB7=JD)@A;dwY9VR#m*R4 zZrWt>_gtrTmJ6C)WooK+jlH(@l%dUOW)9nzD5rBMGLYV9ibJ&by<=XNDz@0G9QtqoY!F~$&qPHU_HqYxkV+XnUfp6vJ~sEuw;VgR+kL1DN9UvS%s6i zxU4c2Bm!$pR*)s8ad?3q!SviLyVm4?3k~Rd(R#=_QhHf~G0aG*zhg1x zp&8)j%~i|y2+7L>eW%ZxF$I1nK>lPb{3adb?Zv?O!&7%?$KIDG5`_?c2o2{VI#WmV z>d*LxDpHu{D!eZ!abZTob6DzYY*R8poMliigS!&`BY}Bu1pj*DAIpTA!CyIV!=I}* zR8Jf3s%1Us#;Vs0>!w~DPJUXo-`iEsRz>@}YGDt$msiu>UECwY?Jaz@;QJ+>gEv3F zkE^Pj?;KZ6NE2_>gYG9)(}pM289iQ2yXw8rcTQ66(VMsh*i}!7>xURJGU5C_N&HO2 ztLdcr(*U}U!*$goy?ab|RoG#elO<%UOPoYnqnc}LRz3Fa{a{ZVl z|JjI9h;wJTYwVCwi23f4bNdylNz=~lH(pg^D1YCiK4#!-r%;XV!E~RjU>=55Gi3b` zx^D>1-<`;q8>{S@MP5@%%Kdc-he5Sfw@>-#0um>Oz$Ch7us=#aXkDiZhGN!0m0 z!x-|++5xbQ|BBA5hmg5b^t=Xoc2%KhvbKzRySbL`ahuUSRP@;(`Yi81({ z-7b|poGI~~(Tf`H4qdtGurj)LmC`+Z^lW_cqnTOKx(w-2kqT9{NaqZsUw2g_(!xecMf}ZeguK0hQYn5-u@%yv2UEf~ z64Ye~f8x+$1gb!bWO^4vqTgtB6r?YvMW&BYmHgT}q|@rV<28j!ew!H5qq-;rE?{+)N!I(jASwZ=^a>q;iqQ@mDGla)U_I)fJNK zj|Ma3EOou)^ZTh<`nyQQ({3ZNWU}Hb|RhENa<`fU!-p& zY(*^34W{44YCoih@0e7)wb^-)4iYIgjMCL2byq)3d~35Y=vB4fy0P@TNiCOr zt{X(ZTa5IN%_-fjP7=R+$5VP(T`JN84yBjX!y{D7@hJ5>fI2n;HM#jdyj$ik=*xV?cm<$LHb1Qn~jiT)MrN8L!{5uR}#yu6BaZn z^@YOGD8%x+!3%J1=?e@T)ck7rjZyzr>qR66cwNDGd&t?QlHqIbg-;iTKTz^F@k5Y77kvT_Msmkw%C9 zCekfNx<#bpjdYtxz09B#6whFy1(yNV>W`;fxX_QD?hdvkWsv-2-HuR08w_E=gMEXuj+jj(| z1))UvC|KlO()?wq(d_jI?$frJF)6 z;&+w!{WWy7NMA@Se+wNi(iri(HMB~k^&;IKI!~mzBHaF`Q=unC>LY&7gq{x{1uIBaem}Zr=-JTA;`fup@_guZkrEQ}<B_ zD1H%VTaj)Pzl5_;q=zJyLT5jbhDpdG#}{d!gzV*zpf6co3z2&IJ*+ z&1;5Hs&g)mu>HM=kP6Zz67rzQ+V3)v`c9xU$oYGO_Oi!NN<*DH#czznxutWjNNJJA zIS+_*v!p)WStC-J_)Tyg5$RL$o8UYa2_sI7g7|HqiO!QE;mH}&Bs_5M1AeK zQ^oS_<$M=$ptFe+DDC5H2;8^76OU4#I?e-}q)0lwgPi<;-@#5{z|V8K2mF>gMIyal z$rOC2BoK0$Qytx|_3%mOg=>{JcwJGgx^?J#a8Dlm5zbTej-hp`mEsO5ARd>bTh*QJ z_Hnwy#dly4aiO?RbpIUpt`_&1{BMEp61QUv?U`CtzS)oM(p{cl%<*EnvkN!CcXS?c ztc31_e7f%xxHv7bP;&U892{Qs}!wmKQ>`PanQmo-?hzaYu6UCdpBzJEz#{_u_axudkU%BpuMe5O$DQ^D;UF! z?puRDCPjC7VjkSXi|DQr{p(bYkW_Tcw@NBH2A%png-yk-dxEo5@%+}YwF}`M*q83U zQ|Nx>&>h}HH&RJ=Zl8tO@NDjQ=$>T48OS^4rtKSdX2`TzwGargOKzhgv3N8h|eTwb+Zt1}; zmws|WG41n>Ji2csc<;T!>o7VzecPt8u~9%rmqzM}z}YS0UZIy4?sj*jol; z>m&X7#(XbM-Hdyu6w&IY72FQDHt!y|XCzpz&+{LITPLNyLR#p0(aF(L(sI$#TM62> zF8f2``<#ToRBV2txLTj%;j6XLHqM~-f-&Uo3aMOdzeJz9?dMuwPD_uVT+0(rz&*T( zuC}z*sb_(MeT%+#W6YnWyt73!+FH8F=%%_+dpQf4-+Ef#^TP96|0?d2;vORIec~P| z?q%Z68N-l&8Bh0YaZeQY3UNOfP5#!+>7Fm{DdJuu?rORB*@^VMQQQl~y-(a4x%b&Y z^u0~o>%?6n?wcdX&+kQdlDMPAoeg)W+GWIRDA8Y}E$9*Fs6zJJ+vF_-ult$cc(Gi1 zt#$5)@D0V-Q+*Y8(eBdHj$(z&i-)S>@TbKob@QYzfX9v;ftE6;mKL%*^srs)&Qs{V z@6a94MEAQ2y3_m6<;=?Fr(@~|OWS#@i2W7Q3aOWbIiy$^vEJPh#Cl$#OLRr?*JO&= zBL35jJ^689YE}2aKLFF$_A?DmxMY6oWqs(bDGg&nt?%lr9Wl<&^TQ=Jo?^*~)|GZ& zl<_F2J>3s%jM=n#1^ds|e5Srnce<^*%bl=y6u(Q2Xk;H11meIuAPJwgFvfCYWx=UPb3Y^T9F(?LqtI<}JC= zIj1E(_xq)~(z)$iW4gt)ZFjoW1lwA_F58-JiMlt~4{dP1wBqerpB`CIGE_we7nO`u zLq}D>eP<-yAH{7Era;{N#GNQ^FLCF=4dyL~UA4ZhSBa}$88sTC{}89HqzvoW^@efP zse^_A|9AW-xSxi$fLpo6J;knit{n3;FcZNf+-ZR@+XGJ-vSWZ*2>kBwV3<9T^C8Z@ zNNZblAlz}GWx})qACJ`Odo1uZl9R$ICn1K_cs68;e*kXFoQZZ9lb-g`0?sk0w~*>C zqqBaJ+CfHPj@s?-J+DJ-7k6N;=p_EO@vP{74enZAdnw|79J3~9pk`gswl!E%P{&mq zJM0F;aJrUM*_72KVRd?~_^CU4K7ifLwCpJyr;e|Bq9m@C_tkzaRj-y5sOPbkf>_j= zst-zv)#KO&P->NGoPJ@rO#N4+=Jc3RS8BMp{_Y9s34DjMQ4TC{(3J8|g%RXRAt0G*aELMWJdn!$^aNb;s_; z_C^{LT@>n}b~e)VXm`~^?QNvLA)h_ffkt`&`Ru67lnGMBaC$G*zWj>_pwGg z1%AENsYW^)r4@RPP`>C*O zpB@*dcI#B0k*M7|RbnJ+x4-IPBx<+6s?(CBFhDgKKc+B1ZDu5rU4X+~lS zX*I`4Oktqf(MU{TpxQ%Al0v=OPo%R|-==P*^{R)QjXzsmF}0$!Q5_@FDmA02f9W8a z9RB_h{h;TN(!pAQ^kvUcr9;$15|S-oT8`A_Tj^-^lSsM+j8%~a-lto@SXE#owt&qQeoaIC z*a9|Jy^K^TjfUX?L}S8f>H^CN2tXsYV&;l!@K(JG&E%^k(1vOUJ3LjPyz0 zLm+Kuq;3_9LgUp=Myja5D!JOrNc&A)6q=w8Fw$XDyQ>K*W2Ejq7lkIOW+T<~#JZe1 z#z@chNrWb;Q;hUMpHb0C>MSE&2`iYaE;7>1u!70zY9lQ`8K$TkjdTFYFh#945?kC< zb+3`w;-;!cjKmf!!~LUBe6YfqxRR5q&`O-V*F;K z{^qE|jkE*mZ;txCk#6g;D739Q$w&|M=#J6t3@u44+o=nT-+(cfmTsr6Fw*cb*Fw79 zNS}?lxpaH=cO(5U<}OHgYe{06tJWAl*85!bgppY9bJdGRqV4XW)*6YnyMw}yI*2Bi z`aJcyk(l~C^_`X^h51ThN09Ak*7$#v&R20G%^&|5q?DE<L0yWG?%*z6`xsh0goz!F_u?#z@*+ybJ+F9*jB(|fS)viWjTiHeJVSYLcg<}xiwX?IuG z8b6kHclB2zv9!CZJB-BA?x7wq5=*;>ddx^H?Vjp6BeAr5s@ILg((a|+F%nC=m-^I5 zEbZRvYa_9=d#m+EVrdsCS32V3;wF&ox;#l=j z=@K=5;kksce7DD%~Xq58hlowBlwx>BU$;+)$Y zs;)N@=Qf9`Ta3igE>m|KiKShp{$V7R_AvFNkyzTp)JsPCuJ5AI@6^AH6zhjQc=dsi z9_pD0EmvO{>Div6qRZ9yMsmkKQ+l`x;oK7IuX5}wkdj8aaCjoLLKPY5_Ti(VD^#_S z_8tCAX|w8Uq@#wv0;$nR3wu6OdW0Hbq@_JyfwZNOW)10E)}rt&ZAovpA&ro>F;eYj z4@Ot2`9>PFSznws+g(diV@ImR#&2s_-Hxjkms#=Xi?Y62DjYRDp zrB)e<+C560ZzO8>_v&&ZQMOCV-OUJ3tjYRDpuf8=BwR^n65gySQYwQFSHxg^?1eMZ~Xz4^% zY5b_A6IHE|sHGECy^+`#oTP>siG9IIYI7s8FF09EHWK@Slhte^QM;$89gIZno}zX& z6197(+Q&%L?x|{tk*M9%)L}-Vc284B8i`u^gF4K!Ao z=Q~q+NwaiG=;6NqWl1}|2p89!?9Vs*BWsKJZXB}QUtFHwIo5=(oD`iqfR z+Dp}KMq+6%Rrec-rM*l&Y9yBSGWD#HSlY|gt43mJFIR6HiKV?leQYF__6qeMElIAg zR6k{<8%nNJqc;=1;CXsv+108Mui~S|@H{=K>>4=}OX>Kkt;_zbdTgovTB;V5U9YC# zU>yC{RP9}MqxxwArK8jlwFi~`Mcp@v(kivph?B}ztDh!o$*;M%><)G26fJ$-|EjV( z)sU%_j#B%(e=WOPZ4hads_*ka*#oL#nhwcl^fhXRNO~3EAL;}xA=l59J)*Xq&XCOY z8)c8G30rCD_^S8I9#ac&=9hjgRbQ7qqn6Lq(wZu_{6#f-mPqp4|FW83B+i9jRx^ym zr=C~Tc1Ge;&ns$YBXKVLs@ls)oD09I4m1+y(66bCkvNBbO&wvR9nyv?$$O zy{=9*(lvF9LT{+EjC6ZlclCz4*hrs*7lqcUtBv#x^0`*sWF*?eztn0Y(I)<-?laQj zxKz5cVyA&|!~?px`zxbc6^|c7lSabXbHaE{F<>O9v2*f{ObB z+7VRZ?zoSroxv^Qf}=C;`rXf62WW@!f6f1W=lkCG`mVp%@SFR7>eSh)&Z$$C?&`uO z>3hsi&aEm;hYK9X{^WV~p-J-{d-p@r+&S&t4^3<5v-l-?4;G%qX`l)GH_b6EoJij9&S0r#e^DKPUZD6LIdP{`qiazGE)+X13dw)uMNL zy;t%W`lFBFc<7Ko1N1ZeQq9b zPFwW3+3wtBI48a^PditIbK(p0l5=`&zcg<;r^oh7^MP}NaDIMeK5=d=&d;yRKHsrs z`^@)loA&iS{vZa%L#p=mJ`)<_j{rR*zc$UB(=+mGljYo$4zITOn`!6VB^};@>+Cz0 z@QvwBr|ZZe>ED>2o!c;CRQk7O^6^^2DpT5LLi*p$=g#TM@gJtkSif|=7yZukc23Wb z@66H8X|2CEBb?J(e{YU=PW$r*bFy>VpFfz1&MiBtti_LJigVW-H5+c0b2=ygWac}k zbMjB7g6}v$@^h#`gVe?r|x#o7S!!npz7 z#B{N(ojcZ>3U`!q8QA98uFmCQn`isc`E3u_GyQFBd%%{ul-l-yo$j2rJz&pwPTL-| zmpG?w58CC<=`l*NS2?H0D8;UIPS3gqHs+k3bq#E_b9!E<+O5v%d7Ww>b56%Y$Ufzq zj)#za-Z`D?8rs*K)48sp-R;~!tgjo{51ku_^>rirg>%|xY4-2VX`iLpU!BuFYiv_a z@OxhStg$_e&hN7(wvWG!eb&St?NVx=HL)X{(>`lrk9SV5D~H*Wozv^eVRoW(`ZTSn zo#LE6O>1grIj84DGdtfoJtvyk3g>hsez>i4PFLcG+iRRVEMrdk5%xysGBW1FZE#N4 zFU{>;&guH4xqZO7rmYvHx3Jrs%Wl0C?rG=rY1)zYMd$Qs+L886=XCv&Zr^uK*DvYz z6X$ewm|?$iPFIH+_Iu~_IN&WNC;EM@$05^(oYUiwWt%yt$05sRIj2w4TH1Eb>C?29 zwzG3>0+*$?vb~%u2wV#{$hqr!-%JwR3v*bg?%%r)N)r-RPX2Jq30P zoqvqF+FE}b+t(GJ0oDJW2|n1qg*oU(XSKj*)c0>2likAfJm6NfZDD#JaASM6Fn#Uo z%=3D)Z@_Em{q0#3bkFO}=fmGmA7rP~Rj|Avb{^fwR?8b=FK|xFE3ylm)AEMdh0bYt zBkaY_X?dgUBDz%>m-qZ6z1Ut(r>o|@aGPC9E#VmZigQ}RarRs1w1ner#z|VwkFAz4 z&JJ=;OE~dB`^Gf?Tlxfh7Pn~$Kc$~!FY=w~ainCNY`>@TSNCVwPw_xhR|2~7FR>N) z?jqJ9*e^R{l8w=QY&FkPI|wh3)08jt>y&Yh)z{stTbbE6W2$|H?$Xp6oIU5-x1D

kW9nY7!cDCDgRf}bIu3g~VEqE2xT)Wu0 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 literal 75264 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 M{r~^{zsG_94fAcHbN~PV diff --git a/docs/DesignSpec.md b/docs/DesignSpec.md index 030f81d..8b1a552 100644 --- a/docs/DesignSpec.md +++ b/docs/DesignSpec.md @@ -6,15 +6,38 @@ The goal is to establish a strong, reusable, secure framework first, then initially implement secret retrieval and export workflows. -Initial public cmdlets: +Public cmdlets: ```powershell Connect-Infisical Disconnect-Infisical Get-InfisicalSecrets Get-InfisicalSecret +New-InfisicalSecret +Update-InfisicalSecret +Remove-InfisicalSecret ConvertTo-InfisicalSecretDictionary Export-InfisicalSecrets +Get-InfisicalProjects +Get-InfisicalProject +New-InfisicalProject +Update-InfisicalProject +Remove-InfisicalProject +Get-InfisicalEnvironments +Get-InfisicalEnvironment +New-InfisicalEnvironment +Update-InfisicalEnvironment +Remove-InfisicalEnvironment +Get-InfisicalFolders +Get-InfisicalFolder +New-InfisicalFolder +Update-InfisicalFolder +Remove-InfisicalFolder +Get-InfisicalTags +Get-InfisicalTag +New-InfisicalTag +Update-InfisicalTag +Remove-InfisicalTag ``` Infisical’s public API is REST-based and provides programmatic access for managing secrets and related resources. Current Infisical documentation shows the list-secrets endpoint under `/api/v4/secrets`, the single-secret retrieval endpoint under `/api/v4/secrets/{secretName}`, and Universal Auth login under `/api/v1/auth/universal-auth/login`. The implementation must centralize API endpoint definitions because Infisical uses different API versions across resource families. ([Infisical Blog][1]) @@ -198,8 +221,31 @@ Example shape: 'Disconnect-Infisical', 'Get-InfisicalSecrets', 'Get-InfisicalSecret', + 'New-InfisicalSecret', + 'Update-InfisicalSecret', + 'Remove-InfisicalSecret', 'ConvertTo-InfisicalSecretDictionary', - 'Export-InfisicalSecrets' + 'Export-InfisicalSecrets', + 'Get-InfisicalProjects', + 'Get-InfisicalProject', + 'New-InfisicalProject', + 'Update-InfisicalProject', + 'Remove-InfisicalProject', + 'Get-InfisicalEnvironments', + 'Get-InfisicalEnvironment', + 'New-InfisicalEnvironment', + 'Update-InfisicalEnvironment', + 'Remove-InfisicalEnvironment', + 'Get-InfisicalFolders', + 'Get-InfisicalFolder', + 'New-InfisicalFolder', + 'Update-InfisicalFolder', + 'Remove-InfisicalFolder', + 'Get-InfisicalTags', + 'Get-InfisicalTag', + 'New-InfisicalTag', + 'Update-InfisicalTag', + 'Remove-InfisicalTag' ) AliasesToExport = @() PrivateData = @{ @@ -698,29 +744,29 @@ Internal implementation must still use proper typed path handling. # 12. Authentication Design -## 12.1 Supported Initial Auth Types +## 12.1 Supported Auth Types -Initial implementation: +Currently implemented: ```text Universal Auth Token Auth +JWT Auth +OIDC Auth +LDAP Auth +Azure Auth +GCP IAM Auth ``` -Infisical documents identity authentication modes such as Universal Auth and Token Auth for API access, and API interaction requires an access token. ([Infisical Blog][3]) +Each implemented provider is exposed as a dedicated `Connect-Infisical` parameter set. Identity-based providers (JWT, OIDC, Azure, GCP IAM) share a common login flow via `IdentityLoginExecutor` and POST to `/api/v1/auth/{provider}-auth/login`. Infisical documents identity authentication modes such as Universal Auth and Token Auth for API access, and API interaction requires an access token. ([Infisical Blog][3]) ## 12.2 Future Auth Types Design must allow future support for: ```text -AWS Auth -Azure Auth -GCP Auth +AWS IAM Auth Kubernetes Auth -OIDC Auth -JWT Auth -LDAP Auth TLS Certificate Auth Alibaba Cloud Auth OCI Auth -- 2.52.0 From e0a6ef02df3e5c4713492535932a66772548c61a Mon Sep 17 00:00:00 2001 From: GraceSolutions Date: Wed, 3 Jun 2026 19:59:11 -0400 Subject: [PATCH 10/12] M9: bulk + duplicate + connection inheritance - Bulk parameter sets on New-/Update-/Remove-InfisicalSecret via v3/secrets/batch/raw. - Copy-InfisicalSecret cmdlet wrapping v4/secrets/duplicate. - InfisicalCmdletBase.Resolve{ProjectId,Environment,SecretPath,ApiVersion,OrganizationId} with verbose inheritance logging. - All resource cmdlets refactored to use the resolution helpers. - InfisicalBulkSecretConverter for flexible Hashtable -> DTO mapping. - 22 new unit tests covering registry, DTOs, converter, and inheritance helpers. Total: 161 passing. --- CHANGELOG.md | 19 ++ Module/PSInfisicalAPI/PSInfisicalAPI.psd1 | 5 +- Module/PSInfisicalAPI/bin/PSInfisicalAPI.dll | Bin 151552 -> 177152 bytes build.ps1 | 1 + docs/DesignSpec.md | 2 + .../BulkSecretConverterTests.cs | 96 ++++++++ .../BulkSecretDtoTests.cs | 137 +++++++++++ .../CmdletBaseInheritanceTests.cs | 143 ++++++++++++ .../EndpointRegistryTests.cs | 4 + .../Cmdlets/CopyInfisicalSecretCmdlet.cs | 75 ++++++ .../Cmdlets/GetInfisicalEnvironmentCmdlet.cs | 3 +- .../Cmdlets/GetInfisicalEnvironmentsCmdlet.cs | 3 +- .../Cmdlets/GetInfisicalFolderCmdlet.cs | 5 +- .../Cmdlets/GetInfisicalFoldersCmdlet.cs | 5 +- .../Cmdlets/GetInfisicalProjectCmdlet.cs | 5 +- .../Cmdlets/GetInfisicalSecretCmdlet.cs | 8 +- .../Cmdlets/GetInfisicalSecretsCmdlet.cs | 8 +- .../Cmdlets/GetInfisicalTagCmdlet.cs | 3 +- .../Cmdlets/GetInfisicalTagsCmdlet.cs | 3 +- .../Cmdlets/InfisicalCmdletBase.cs | 39 ++++ .../Cmdlets/NewInfisicalEnvironmentCmdlet.cs | 3 +- .../Cmdlets/NewInfisicalFolderCmdlet.cs | 5 +- .../Cmdlets/NewInfisicalSecretCmdlet.cs | 53 ++++- .../Cmdlets/NewInfisicalTagCmdlet.cs | 3 +- .../RemoveInfisicalEnvironmentCmdlet.cs | 3 +- .../Cmdlets/RemoveInfisicalFolderCmdlet.cs | 5 +- .../Cmdlets/RemoveInfisicalProjectCmdlet.cs | 12 +- .../Cmdlets/RemoveInfisicalSecretCmdlet.cs | 50 +++- .../Cmdlets/RemoveInfisicalTagCmdlet.cs | 3 +- .../UpdateInfisicalEnvironmentCmdlet.cs | 3 +- .../Cmdlets/UpdateInfisicalFolderCmdlet.cs | 5 +- .../Cmdlets/UpdateInfisicalProjectCmdlet.cs | 10 +- .../Cmdlets/UpdateInfisicalSecretCmdlet.cs | 52 ++++- .../Cmdlets/UpdateInfisicalTagCmdlet.cs | 3 +- .../Endpoints/InfisicalEndpointNames.cs | 4 + .../Endpoints/InfisicalEndpointRegistry.cs | 48 ++++ .../Secrets/InfisicalBulkSecretConverter.cs | 164 +++++++++++++ .../Secrets/InfisicalSecretDtos.cs | 71 ++++++ .../Secrets/InfisicalSecretQuery.cs | 65 ++++++ .../Secrets/InfisicalSecretsClient.cs | 221 ++++++++++++++++++ 40 files changed, 1282 insertions(+), 65 deletions(-) create mode 100644 src/PSInfisicalAPI.Tests/BulkSecretConverterTests.cs create mode 100644 src/PSInfisicalAPI.Tests/BulkSecretDtoTests.cs create mode 100644 src/PSInfisicalAPI.Tests/CmdletBaseInheritanceTests.cs create mode 100644 src/PSInfisicalAPI/Cmdlets/CopyInfisicalSecretCmdlet.cs create mode 100644 src/PSInfisicalAPI/Secrets/InfisicalBulkSecretConverter.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index dd1a5e3..251ad61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,25 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) loos ## Unreleased +## 2026.06.03.2207 + +- Build produced from commit 09c3d5c68bbc. +- **M9 — Bulk, Duplicate & Inheritance**: + - **Bulk parameter sets** added to `New-InfisicalSecret`, `Update-InfisicalSecret`, and `Remove-InfisicalSecret` accepting `-Secrets Hashtable[]`; client methods `CreateBatch`/`UpdateBatch`/`DeleteBatch` wrap `POST|PATCH|DELETE /api/v3/secrets/batch/raw`. + - **`Copy-InfisicalSecret`** cmdlet added, wrapping `POST /api/v4/secrets/duplicate` with source/destination environment + path parameters and per-attribute copy toggles. + - **Connection inheritance** centralized in `InfisicalCmdletBase` (`ResolveProjectId`/`ResolveEnvironment`/`ResolveSecretPath`/`ResolveApiVersion`/`ResolveOrganizationId`). Explicit parameters always win; missing values fall back to the active connection and emit a `-Verbose` line. + - Project/Environment/Folder/Tag and all secret cmdlets refactored to use the inheritance helpers; existing explicit-parameter behavior is preserved. + - `InfisicalBulkSecretConverter` accepts flexible key aliases (`SecretName`/`Name`/`Key`, `SecretValue`/`Value`, `SecretComment`/`Comment`, `Metadata`/`SecretMetadata`). + - Test count: 161 (up from 139). Added coverage for bulk DTO shapes, the converter, the duplicate request DTO, registry entries for the four new endpoints, and the resolution helpers. + +## Unreleased (carried forward) + +## 2026.06.03.2206 + +- Build produced from commit 09c3d5c68bbc. + +## Unreleased (carried forward) + ## 2026.06.03.2136 - Build produced from commit d9822aab7a4a. diff --git a/Module/PSInfisicalAPI/PSInfisicalAPI.psd1 b/Module/PSInfisicalAPI/PSInfisicalAPI.psd1 index 3e80f15..bd0557e 100644 --- a/Module/PSInfisicalAPI/PSInfisicalAPI.psd1 +++ b/Module/PSInfisicalAPI/PSInfisicalAPI.psd1 @@ -1,6 +1,6 @@ @{ RootModule = 'PSInfisicalAPI.psm1' - ModuleVersion = '2026.06.03.2136' + ModuleVersion = '2026.06.03.2207' GUID = 'b8a2f3d4-7c51-4d2f-9e6a-1f0c8b3d4e51' Author = 'Grace Solutions' CompanyName = 'Grace Solutions' @@ -17,6 +17,7 @@ 'New-InfisicalSecret', 'Update-InfisicalSecret', 'Remove-InfisicalSecret', + 'Copy-InfisicalSecret', 'ConvertTo-InfisicalSecretDictionary', 'Export-InfisicalSecrets', 'Get-InfisicalProjects', @@ -50,7 +51,7 @@ LicenseUri = 'https://www.gnu.org/licenses/agpl-3.0.html' ProjectUri = 'https://prod.git.gracesolution.info/gsadmin/PSInfisicalAPI' ReleaseNotes = 'See CHANGELOG.md in the project repository for release history.' - CommitHash = 'd9822aab7a4a' + CommitHash = '09c3d5c68bbc' } } } \ No newline at end of file diff --git a/Module/PSInfisicalAPI/bin/PSInfisicalAPI.dll b/Module/PSInfisicalAPI/bin/PSInfisicalAPI.dll index 72da4ed07bb9ab1b8de515b598b68715f5e23e56..ca605fc20afc9c2116736db3c473c1507b503b87 100644 GIT binary patch literal 177152 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 (1Nx7WEiHgJ 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

Xp`fH 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

kW9nY7!cDCDgRf}bIu3g~VEqE2xT)Wu0 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 diff --git a/build.ps1 b/build.ps1 index c1457a1..6a0066d 100644 --- a/build.ps1 +++ b/build.ps1 @@ -105,6 +105,7 @@ function Write-Manifest { 'New-InfisicalSecret', 'Update-InfisicalSecret', 'Remove-InfisicalSecret', + 'Copy-InfisicalSecret', 'ConvertTo-InfisicalSecretDictionary', 'Export-InfisicalSecrets', 'Get-InfisicalProjects', diff --git a/docs/DesignSpec.md b/docs/DesignSpec.md index 8b1a552..a7124fd 100644 --- a/docs/DesignSpec.md +++ b/docs/DesignSpec.md @@ -16,6 +16,7 @@ Get-InfisicalSecret New-InfisicalSecret Update-InfisicalSecret Remove-InfisicalSecret +Copy-InfisicalSecret ConvertTo-InfisicalSecretDictionary Export-InfisicalSecrets Get-InfisicalProjects @@ -224,6 +225,7 @@ Example shape: 'New-InfisicalSecret', 'Update-InfisicalSecret', 'Remove-InfisicalSecret', + 'Copy-InfisicalSecret', 'ConvertTo-InfisicalSecretDictionary', 'Export-InfisicalSecrets', 'Get-InfisicalProjects', diff --git a/src/PSInfisicalAPI.Tests/BulkSecretConverterTests.cs b/src/PSInfisicalAPI.Tests/BulkSecretConverterTests.cs new file mode 100644 index 0000000..7972862 --- /dev/null +++ b/src/PSInfisicalAPI.Tests/BulkSecretConverterTests.cs @@ -0,0 +1,96 @@ +using System.Collections; +using PSInfisicalAPI.Errors; +using PSInfisicalAPI.Secrets; +using Xunit; + +namespace PSInfisicalAPI.Tests +{ + public class BulkSecretConverterTests + { + [Fact] + public void ToCreateItems_Maps_Standard_Keys() + { + Hashtable entry = new Hashtable + { + { "SecretName", "API_KEY" }, + { "SecretValue", "abc" }, + { "SecretComment", "primary" }, + { "SkipMultilineEncoding", true }, + { "TagIds", new[] { "tag-1", "tag-2" } } + }; + + InfisicalBulkCreateSecretItem[] items = InfisicalBulkSecretConverter.ToCreateItems(new[] { entry }); + Assert.Single(items); + Assert.Equal("API_KEY", items[0].SecretName); + Assert.Equal("abc", items[0].SecretValue); + Assert.Equal("primary", items[0].SecretComment); + Assert.True(items[0].SkipMultilineEncoding); + Assert.Equal(new[] { "tag-1", "tag-2" }, items[0].TagIds); + } + + [Fact] + public void ToCreateItems_Accepts_Name_Alias_For_SecretName() + { + Hashtable entry = new Hashtable + { + { "Name", "API_KEY" }, + { "Value", "abc" } + }; + + InfisicalBulkCreateSecretItem[] items = InfisicalBulkSecretConverter.ToCreateItems(new[] { entry }); + Assert.Single(items); + Assert.Equal("API_KEY", items[0].SecretName); + Assert.Equal("abc", items[0].SecretValue); + } + + [Fact] + public void ToCreateItems_Without_SecretName_Throws() + { + Hashtable entry = new Hashtable { { "Value", "abc" } }; + + Assert.Throws(() => + InfisicalBulkSecretConverter.ToCreateItems(new[] { entry })); + } + + [Fact] + public void ToUpdateItems_Maps_NewSecretName() + { + Hashtable entry = new Hashtable + { + { "SecretName", "API_KEY" }, + { "NewSecretName", "RENAMED" }, + { "SecretValue", "new-value" } + }; + + InfisicalBulkUpdateSecretItem[] items = InfisicalBulkSecretConverter.ToUpdateItems(new[] { entry }); + Assert.Single(items); + Assert.Equal("API_KEY", items[0].SecretName); + Assert.Equal("RENAMED", items[0].NewSecretName); + Assert.Equal("new-value", items[0].SecretValue); + } + + [Fact] + public void ToCreateItems_Maps_Metadata_Dictionary() + { + Hashtable meta = new Hashtable { { "owner", "platform" }, { "tier", "1" } }; + Hashtable entry = new Hashtable + { + { "SecretName", "API_KEY" }, + { "SecretValue", "abc" }, + { "Metadata", meta } + }; + + InfisicalBulkCreateSecretItem[] items = InfisicalBulkSecretConverter.ToCreateItems(new[] { entry }); + Assert.NotNull(items[0].SecretMetadata); + Assert.Equal("platform", items[0].SecretMetadata["owner"]); + Assert.Equal("1", items[0].SecretMetadata["tier"]); + } + + [Fact] + public void ToCreateItems_Empty_Input_Returns_Empty() + { + InfisicalBulkCreateSecretItem[] items = InfisicalBulkSecretConverter.ToCreateItems(null); + Assert.Empty(items); + } + } +} diff --git a/src/PSInfisicalAPI.Tests/BulkSecretDtoTests.cs b/src/PSInfisicalAPI.Tests/BulkSecretDtoTests.cs new file mode 100644 index 0000000..b4a4474 --- /dev/null +++ b/src/PSInfisicalAPI.Tests/BulkSecretDtoTests.cs @@ -0,0 +1,137 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Xunit; + +namespace PSInfisicalAPI.Tests +{ + public class BulkSecretDtoTests + { + private static readonly System.Reflection.Assembly ModuleAssembly = + typeof(PSInfisicalAPI.Connections.InfisicalConnection).Assembly; + + private static object MakeDto(string typeName) + { + System.Type t = ModuleAssembly.GetType(typeName, true); + return System.Activator.CreateInstance(t); + } + + [Fact] + public void BatchCreateItem_Serializes_With_Expected_Field_Names() + { + object item = MakeDto("PSInfisicalAPI.Secrets.InfisicalSecretBatchCreateItemDto"); + item.GetType().GetProperty("SecretKey").SetValue(item, "API_KEY"); + item.GetType().GetProperty("SecretValue").SetValue(item, "abc"); + + JObject json = JObject.Parse(JsonConvert.SerializeObject(item)); + Assert.Equal("API_KEY", (string)json["secretKey"]); + Assert.Equal("abc", (string)json["secretValue"]); + Assert.False(json.ContainsKey("skipMultilineEncoding")); + Assert.False(json.ContainsKey("tagIds")); + Assert.False(json.ContainsKey("secretMetadata")); + } + + [Fact] + public void BatchUpdateItem_Omits_Null_Optional_Fields() + { + object item = MakeDto("PSInfisicalAPI.Secrets.InfisicalSecretBatchUpdateItemDto"); + item.GetType().GetProperty("SecretKey").SetValue(item, "API_KEY"); + item.GetType().GetProperty("NewSecretName").SetValue(item, "RENAMED"); + + JObject json = JObject.Parse(JsonConvert.SerializeObject(item)); + Assert.Equal("API_KEY", (string)json["secretKey"]); + Assert.Equal("RENAMED", (string)json["newSecretName"]); + Assert.False(json.ContainsKey("secretValue")); + Assert.False(json.ContainsKey("secretComment")); + } + + [Fact] + public void BatchCreateRequest_Serializes_With_Expected_Envelope() + { + System.Type itemType = ModuleAssembly.GetType("PSInfisicalAPI.Secrets.InfisicalSecretBatchCreateItemDto", true); + object item = System.Activator.CreateInstance(itemType); + itemType.GetProperty("SecretKey").SetValue(item, "K1"); + itemType.GetProperty("SecretValue").SetValue(item, "V1"); + + System.Type listType = typeof(List<>).MakeGenericType(itemType); + object list = System.Activator.CreateInstance(listType); + listType.GetMethod("Add").Invoke(list, new object[] { item }); + + object dto = MakeDto("PSInfisicalAPI.Secrets.InfisicalSecretBatchCreateRequestDto"); + dto.GetType().GetProperty("WorkspaceId").SetValue(dto, "wks-1"); + dto.GetType().GetProperty("Environment").SetValue(dto, "prod"); + dto.GetType().GetProperty("SecretPath").SetValue(dto, "/db"); + dto.GetType().GetProperty("Secrets").SetValue(dto, list); + + JObject json = JObject.Parse(JsonConvert.SerializeObject(dto)); + Assert.Equal("wks-1", (string)json["workspaceId"]); + Assert.Equal("prod", (string)json["environment"]); + Assert.Equal("/db", (string)json["secretPath"]); + JArray secrets = (JArray)json["secrets"]; + Assert.Single(secrets); + Assert.Equal("K1", (string)secrets[0]["secretKey"]); + Assert.Equal("V1", (string)secrets[0]["secretValue"]); + } + + [Fact] + public void BatchDeleteRequest_Serializes_With_Secret_Keys() + { + System.Type itemType = ModuleAssembly.GetType("PSInfisicalAPI.Secrets.InfisicalSecretBatchDeleteItemDto", true); + object item = System.Activator.CreateInstance(itemType); + itemType.GetProperty("SecretKey").SetValue(item, "K1"); + + System.Type listType = typeof(List<>).MakeGenericType(itemType); + object list = System.Activator.CreateInstance(listType); + listType.GetMethod("Add").Invoke(list, new object[] { item }); + + object dto = MakeDto("PSInfisicalAPI.Secrets.InfisicalSecretBatchDeleteRequestDto"); + dto.GetType().GetProperty("WorkspaceId").SetValue(dto, "wks-1"); + dto.GetType().GetProperty("Environment").SetValue(dto, "prod"); + dto.GetType().GetProperty("Secrets").SetValue(dto, list); + + JObject json = JObject.Parse(JsonConvert.SerializeObject(dto)); + Assert.Equal("wks-1", (string)json["workspaceId"]); + JArray secrets = (JArray)json["secrets"]; + Assert.Single(secrets); + Assert.Equal("K1", (string)secrets[0]["secretKey"]); + } + + [Fact] + public void DuplicateRequest_Serializes_With_Expected_Field_Names() + { + object dto = MakeDto("PSInfisicalAPI.Secrets.InfisicalSecretDuplicateRequestDto"); + dto.GetType().GetProperty("ProjectId").SetValue(dto, "wks-1"); + dto.GetType().GetProperty("SourceEnvironment").SetValue(dto, "dev"); + dto.GetType().GetProperty("DestinationEnvironment").SetValue(dto, "prod"); + dto.GetType().GetProperty("SourceSecretPath").SetValue(dto, "/db"); + dto.GetType().GetProperty("DestinationSecretPath").SetValue(dto, "/db"); + dto.GetType().GetProperty("SecretIds").SetValue(dto, new[] { "id-1", "id-2" }); + dto.GetType().GetProperty("OverwriteExisting").SetValue(dto, true); + + JObject json = JObject.Parse(JsonConvert.SerializeObject(dto)); + Assert.Equal("wks-1", (string)json["projectId"]); + Assert.Equal("dev", (string)json["sourceEnvironment"]); + Assert.Equal("prod", (string)json["destinationEnvironment"]); + Assert.Equal("/db", (string)json["sourceSecretPath"]); + Assert.Equal("/db", (string)json["destinationSecretPath"]); + Assert.True((bool)json["overwriteExisting"]); + JArray ids = (JArray)json["secretIds"]; + Assert.Equal(2, ids.Count); + Assert.Equal("id-1", (string)ids[0]); + Assert.False(json.ContainsKey("attributesToCopy")); + } + + [Fact] + public void DuplicateAttributes_Omits_Null_Toggles() + { + object attrs = MakeDto("PSInfisicalAPI.Secrets.InfisicalSecretDuplicateAttributesDto"); + attrs.GetType().GetProperty("SecretValue").SetValue(attrs, true); + + JObject json = JObject.Parse(JsonConvert.SerializeObject(attrs)); + Assert.True((bool)json["secretValue"]); + Assert.False(json.ContainsKey("secretComment")); + Assert.False(json.ContainsKey("tags")); + Assert.False(json.ContainsKey("metadata")); + } + } +} diff --git a/src/PSInfisicalAPI.Tests/CmdletBaseInheritanceTests.cs b/src/PSInfisicalAPI.Tests/CmdletBaseInheritanceTests.cs new file mode 100644 index 0000000..6ae42cd --- /dev/null +++ b/src/PSInfisicalAPI.Tests/CmdletBaseInheritanceTests.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.Management.Automation; +using System.Reflection; +using PSInfisicalAPI.Cmdlets; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Logging; +using PSInfisicalAPI.Models; +using Xunit; + +namespace PSInfisicalAPI.Tests +{ + public class CmdletBaseInheritanceTests + { + private sealed class RecordingLogger : IInfisicalLogger + { + public List VerboseEntries { get; } = new List(); + + public void Information(string component, string message) { } + public void Verbose(string component, string message) { VerboseEntries.Add(message); } + public void Debug(string component, string message) { } + public void Warning(string component, string message) { } + public void Error(string component, string message) { } + } + + [Cmdlet(VerbsCommon.Get, "TestCmdlet")] + private sealed class TestCmdlet : InfisicalCmdletBase + { + public string CallResolveProjectId(InfisicalConnection connection, string explicitValue) + { + return ResolveProjectId(connection, explicitValue); + } + + public string CallResolveEnvironment(InfisicalConnection connection, string explicitValue) + { + return ResolveEnvironment(connection, explicitValue); + } + + public string CallResolveSecretPath(InfisicalConnection connection, string explicitValue) + { + return ResolveSecretPath(connection, explicitValue); + } + + public string CallResolveApiVersion(InfisicalConnection connection, string explicitValue) + { + return ResolveApiVersion(connection, explicitValue); + } + + public string CallResolveOrganizationId(InfisicalConnection connection, string explicitValue) + { + return ResolveOrganizationId(connection, explicitValue); + } + } + + private static TestCmdlet CreateCmdletWith(RecordingLogger logger) + { + TestCmdlet cmdlet = new TestCmdlet(); + FieldInfo field = typeof(InfisicalCmdletBase).GetField("_logger", BindingFlags.NonPublic | BindingFlags.Instance); + field.SetValue(cmdlet, logger); + return cmdlet; + } + + private static InfisicalConnection ConnectionWithDefaults() + { + return new InfisicalConnection + { + BaseUri = new Uri("https://app.example.com"), + ProjectId = "proj-conn", + Environment = "prod-conn", + DefaultSecretPath = "/db", + OrganizationId = "org-conn", + PinnedApiVersion = "v3" + }; + } + + [Fact] + public void Explicit_Value_Overrides_Connection_And_Does_Not_Log() + { + RecordingLogger logger = new RecordingLogger(); + TestCmdlet cmdlet = CreateCmdletWith(logger); + + string resolved = cmdlet.CallResolveProjectId(ConnectionWithDefaults(), "explicit-proj"); + Assert.Equal("explicit-proj", resolved); + Assert.Empty(logger.VerboseEntries); + } + + [Fact] + public void Missing_Value_Inherits_From_Connection_And_Logs() + { + RecordingLogger logger = new RecordingLogger(); + TestCmdlet cmdlet = CreateCmdletWith(logger); + + string resolved = cmdlet.CallResolveProjectId(ConnectionWithDefaults(), null); + Assert.Equal("proj-conn", resolved); + Assert.Single(logger.VerboseEntries); + Assert.Contains("Inherited ProjectId", logger.VerboseEntries[0]); + Assert.Contains("proj-conn", logger.VerboseEntries[0]); + } + + [Fact] + public void ResolveSecretPath_Defaults_To_Root_When_Connection_Has_No_Default() + { + RecordingLogger logger = new RecordingLogger(); + TestCmdlet cmdlet = CreateCmdletWith(logger); + + InfisicalConnection bareConnection = new InfisicalConnection { BaseUri = new Uri("https://app.example.com") }; + string resolved = cmdlet.CallResolveSecretPath(bareConnection, null); + Assert.Equal("/", resolved); + } + + [Fact] + public void ResolveSecretPath_Inherits_From_Connection_When_Set() + { + RecordingLogger logger = new RecordingLogger(); + TestCmdlet cmdlet = CreateCmdletWith(logger); + + string resolved = cmdlet.CallResolveSecretPath(ConnectionWithDefaults(), null); + Assert.Equal("/db", resolved); + Assert.Contains(logger.VerboseEntries, v => v.Contains("SecretPath") && v.Contains("/db")); + } + + [Fact] + public void ResolveApiVersion_Prefers_PinnedApiVersion_From_Connection() + { + RecordingLogger logger = new RecordingLogger(); + TestCmdlet cmdlet = CreateCmdletWith(logger); + + string resolved = cmdlet.CallResolveApiVersion(ConnectionWithDefaults(), null); + Assert.Equal("v3", resolved); + } + + [Fact] + public void ResolveEnvironment_And_ResolveOrganizationId_Inherit() + { + RecordingLogger logger = new RecordingLogger(); + TestCmdlet cmdlet = CreateCmdletWith(logger); + + Assert.Equal("prod-conn", cmdlet.CallResolveEnvironment(ConnectionWithDefaults(), null)); + Assert.Equal("org-conn", cmdlet.CallResolveOrganizationId(ConnectionWithDefaults(), null)); + Assert.Equal(2, logger.VerboseEntries.Count); + } + } +} diff --git a/src/PSInfisicalAPI.Tests/EndpointRegistryTests.cs b/src/PSInfisicalAPI.Tests/EndpointRegistryTests.cs index adeccf3..d931695 100644 --- a/src/PSInfisicalAPI.Tests/EndpointRegistryTests.cs +++ b/src/PSInfisicalAPI.Tests/EndpointRegistryTests.cs @@ -71,6 +71,10 @@ namespace PSInfisicalAPI.Tests [InlineData(InfisicalEndpointNames.LdapAuthLogin, "POST", "/api/v1/auth/ldap-auth/login")] [InlineData(InfisicalEndpointNames.AzureAuthLogin, "POST", "/api/v1/auth/azure-auth/login")] [InlineData(InfisicalEndpointNames.GcpIamAuthLogin, "POST", "/api/v1/auth/gcp-auth/login")] + [InlineData(InfisicalEndpointNames.BulkCreateSecret, "POST", "/api/v3/secrets/batch/raw")] + [InlineData(InfisicalEndpointNames.BulkUpdateSecret, "PATCH", "/api/v3/secrets/batch/raw")] + [InlineData(InfisicalEndpointNames.BulkDeleteSecret, "DELETE", "/api/v3/secrets/batch/raw")] + [InlineData(InfisicalEndpointNames.DuplicateSecret, "POST", "/api/v4/secrets/duplicate")] public void Registered_Endpoints_Have_Expected_Shape(string name, string method, string template) { InfisicalEndpointDefinition definition = InfisicalEndpointRegistry.Get(name); diff --git a/src/PSInfisicalAPI/Cmdlets/CopyInfisicalSecretCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/CopyInfisicalSecretCmdlet.cs new file mode 100644 index 0000000..ce4baea --- /dev/null +++ b/src/PSInfisicalAPI/Cmdlets/CopyInfisicalSecretCmdlet.cs @@ -0,0 +1,75 @@ +using System; +using System.Management.Automation; +using PSInfisicalAPI.Connections; +using PSInfisicalAPI.Models; +using PSInfisicalAPI.Secrets; + +namespace PSInfisicalAPI.Cmdlets +{ + [Cmdlet(VerbsCommon.Copy, "InfisicalSecret", SupportsShouldProcess = true)] + [OutputType(typeof(InfisicalSecret))] + public sealed class CopyInfisicalSecretCmdlet : InfisicalCmdletBase + { + [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] + [Alias("Id", "SecretIds")] + public string[] SecretId { get; set; } + + [Parameter(Mandatory = true)] + public string DestinationEnvironment { get; set; } + + [Parameter] public string DestinationSecretPath { get; set; } + [Parameter] public string SourceEnvironment { get; set; } + [Parameter] public string SourceSecretPath { get; set; } + [Parameter] public string ProjectId { get; set; } + [Parameter] public string ApiVersion { get; set; } + [Parameter] public SwitchParameter OverwriteExisting { get; set; } + [Parameter] public SwitchParameter CopySecretValue { get; set; } + [Parameter] public SwitchParameter CopySecretComment { get; set; } + [Parameter] public SwitchParameter CopyTags { get; set; } + [Parameter] public SwitchParameter CopyMetadata { get; set; } + + protected override void ProcessRecord() + { + try + { + if (SecretId == null || SecretId.Length == 0) { return; } + + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + string resolvedProjectId = ResolveProjectId(connection, ProjectId); + string resolvedSourceEnv = ResolveEnvironment(connection, SourceEnvironment); + string resolvedSourcePath = ResolveSecretPath(connection, SourceSecretPath); + string resolvedApiVersion = ResolveApiVersion(connection, ApiVersion); + + string target = string.Concat(SecretId.Length, " secret(s) -> ", DestinationEnvironment); + if (!ShouldProcess(target, "Duplicate Infisical secrets")) { return; } + + InfisicalDuplicateSecretsRequest request = new InfisicalDuplicateSecretsRequest + { + ProjectId = resolvedProjectId, + SourceEnvironment = resolvedSourceEnv, + DestinationEnvironment = DestinationEnvironment, + SourceSecretPath = resolvedSourcePath, + DestinationSecretPath = DestinationSecretPath, + SecretIds = SecretId, + ApiVersion = resolvedApiVersion, + OverwriteExisting = OverwriteExisting.IsPresent ? (bool?)true : null, + CopySecretValue = CopySecretValue.IsPresent ? (bool?)true : null, + CopySecretComment = CopySecretComment.IsPresent ? (bool?)true : null, + CopyTags = CopyTags.IsPresent ? (bool?)true : null, + CopyMetadata = CopyMetadata.IsPresent ? (bool?)true : null + }; + + InfisicalSecretsClient client = new InfisicalSecretsClient(HttpClient, Logger); + InfisicalSecret[] duplicated = client.Duplicate(connection, request); + if (duplicated != null) + { + foreach (InfisicalSecret secret in duplicated) { WriteObject(secret); } + } + } + catch (Exception exception) + { + ThrowTerminatingForException("CopyInfisicalSecretCmdlet", "DuplicateSecrets", exception); + } + } + } +} diff --git a/src/PSInfisicalAPI/Cmdlets/GetInfisicalEnvironmentCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/GetInfisicalEnvironmentCmdlet.cs index e7f3d59..728ec32 100644 --- a/src/PSInfisicalAPI/Cmdlets/GetInfisicalEnvironmentCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/GetInfisicalEnvironmentCmdlet.cs @@ -21,8 +21,9 @@ namespace PSInfisicalAPI.Cmdlets try { InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + string resolvedProjectId = ResolveProjectId(connection, ProjectId); InfisicalEnvironmentClient client = new InfisicalEnvironmentClient(HttpClient, Logger); - InfisicalEnvironment env = client.Retrieve(connection, ProjectId, EnvironmentSlugOrId); + InfisicalEnvironment env = client.Retrieve(connection, resolvedProjectId, EnvironmentSlugOrId); if (env != null) { WriteObject(env); diff --git a/src/PSInfisicalAPI/Cmdlets/GetInfisicalEnvironmentsCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/GetInfisicalEnvironmentsCmdlet.cs index 23879d9..2128cda 100644 --- a/src/PSInfisicalAPI/Cmdlets/GetInfisicalEnvironmentsCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/GetInfisicalEnvironmentsCmdlet.cs @@ -17,8 +17,9 @@ namespace PSInfisicalAPI.Cmdlets try { InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + string resolvedProjectId = ResolveProjectId(connection, ProjectId); InfisicalEnvironmentClient client = new InfisicalEnvironmentClient(HttpClient, Logger); - InfisicalEnvironment[] envs = client.List(connection, ProjectId); + InfisicalEnvironment[] envs = client.List(connection, resolvedProjectId); foreach (InfisicalEnvironment env in envs) { WriteObject(env); diff --git a/src/PSInfisicalAPI/Cmdlets/GetInfisicalFolderCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/GetInfisicalFolderCmdlet.cs index 30da2af..22ff5ee 100644 --- a/src/PSInfisicalAPI/Cmdlets/GetInfisicalFolderCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/GetInfisicalFolderCmdlet.cs @@ -23,8 +23,11 @@ namespace PSInfisicalAPI.Cmdlets try { InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + string resolvedProjectId = ResolveProjectId(connection, ProjectId); + string resolvedEnvironment = ResolveEnvironment(connection, Environment); + string resolvedPath = ResolveSecretPath(connection, Path); InfisicalFolderClient client = new InfisicalFolderClient(HttpClient, Logger); - InfisicalFolder folder = client.Retrieve(connection, ProjectId, Environment, Path, FolderNameOrId); + InfisicalFolder folder = client.Retrieve(connection, resolvedProjectId, resolvedEnvironment, resolvedPath, FolderNameOrId); if (folder != null) { WriteObject(folder); diff --git a/src/PSInfisicalAPI/Cmdlets/GetInfisicalFoldersCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/GetInfisicalFoldersCmdlet.cs index 7c64a2b..4c3b2a6 100644 --- a/src/PSInfisicalAPI/Cmdlets/GetInfisicalFoldersCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/GetInfisicalFoldersCmdlet.cs @@ -19,8 +19,11 @@ namespace PSInfisicalAPI.Cmdlets try { InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + string resolvedProjectId = ResolveProjectId(connection, ProjectId); + string resolvedEnvironment = ResolveEnvironment(connection, Environment); + string resolvedPath = ResolveSecretPath(connection, Path); InfisicalFolderClient client = new InfisicalFolderClient(HttpClient, Logger); - InfisicalFolder[] folders = client.List(connection, ProjectId, Environment, Path); + InfisicalFolder[] folders = client.List(connection, resolvedProjectId, resolvedEnvironment, resolvedPath); foreach (InfisicalFolder folder in folders) { WriteObject(folder); diff --git a/src/PSInfisicalAPI/Cmdlets/GetInfisicalProjectCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/GetInfisicalProjectCmdlet.cs index 979928d..93ec71e 100644 --- a/src/PSInfisicalAPI/Cmdlets/GetInfisicalProjectCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/GetInfisicalProjectCmdlet.cs @@ -10,7 +10,7 @@ namespace PSInfisicalAPI.Cmdlets [OutputType(typeof(InfisicalProject))] public sealed class GetInfisicalProjectCmdlet : InfisicalCmdletBase { - [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)] + [Parameter(ValueFromPipelineByPropertyName = true, Position = 0)] [Alias("Id")] public string ProjectId { get; set; } @@ -19,8 +19,9 @@ namespace PSInfisicalAPI.Cmdlets try { InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + string resolvedProjectId = ResolveProjectId(connection, ProjectId); InfisicalProjectClient client = new InfisicalProjectClient(HttpClient, Logger); - InfisicalProject project = client.Retrieve(connection, ProjectId); + InfisicalProject project = client.Retrieve(connection, resolvedProjectId); if (project != null) { WriteObject(project); diff --git a/src/PSInfisicalAPI/Cmdlets/GetInfisicalSecretCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/GetInfisicalSecretCmdlet.cs index eae5ea5..2493296 100644 --- a/src/PSInfisicalAPI/Cmdlets/GetInfisicalSecretCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/GetInfisicalSecretCmdlet.cs @@ -32,10 +32,10 @@ namespace PSInfisicalAPI.Cmdlets InfisicalRetrieveSecretQuery query = new InfisicalRetrieveSecretQuery { SecretName = SecretName, - ProjectId = ProjectId, - Environment = Environment, - SecretPath = SecretPath, - ApiVersion = ApiVersion, + ProjectId = ResolveProjectId(connection, ProjectId), + Environment = ResolveEnvironment(connection, Environment), + SecretPath = ResolveSecretPath(connection, SecretPath), + ApiVersion = ResolveApiVersion(connection, ApiVersion), Version = Version, Type = Type.ToString(), ViewSecretValue = ViewSecretValue.IsPresent, diff --git a/src/PSInfisicalAPI/Cmdlets/GetInfisicalSecretsCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/GetInfisicalSecretsCmdlet.cs index e3e60bb..599b8a9 100644 --- a/src/PSInfisicalAPI/Cmdlets/GetInfisicalSecretsCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/GetInfisicalSecretsCmdlet.cs @@ -32,10 +32,10 @@ namespace PSInfisicalAPI.Cmdlets InfisicalListSecretsQuery query = new InfisicalListSecretsQuery { - ProjectId = ProjectId, - Environment = Environment, - SecretPath = SecretPath, - ApiVersion = ApiVersion, + ProjectId = ResolveProjectId(connection, ProjectId), + Environment = ResolveEnvironment(connection, Environment), + SecretPath = ResolveSecretPath(connection, SecretPath), + ApiVersion = ResolveApiVersion(connection, ApiVersion), Recursive = Recursive.IsPresent, IncludeImports = IncludeImports.IsPresent, IncludePersonalOverrides = IncludePersonalOverrides.IsPresent, diff --git a/src/PSInfisicalAPI/Cmdlets/GetInfisicalTagCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/GetInfisicalTagCmdlet.cs index cc8d32e..8c7837f 100644 --- a/src/PSInfisicalAPI/Cmdlets/GetInfisicalTagCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/GetInfisicalTagCmdlet.cs @@ -21,8 +21,9 @@ namespace PSInfisicalAPI.Cmdlets try { InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + string resolvedProjectId = ResolveProjectId(connection, ProjectId); InfisicalTagClient client = new InfisicalTagClient(HttpClient, Logger); - InfisicalTag tag = client.Retrieve(connection, ProjectId, TagSlugOrId); + InfisicalTag tag = client.Retrieve(connection, resolvedProjectId, TagSlugOrId); if (tag != null) { WriteObject(tag); diff --git a/src/PSInfisicalAPI/Cmdlets/GetInfisicalTagsCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/GetInfisicalTagsCmdlet.cs index 8b10649..a4b736c 100644 --- a/src/PSInfisicalAPI/Cmdlets/GetInfisicalTagsCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/GetInfisicalTagsCmdlet.cs @@ -17,8 +17,9 @@ namespace PSInfisicalAPI.Cmdlets try { InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + string resolvedProjectId = ResolveProjectId(connection, ProjectId); InfisicalTagClient client = new InfisicalTagClient(HttpClient, Logger); - InfisicalTag[] tags = client.List(connection, ProjectId); + InfisicalTag[] tags = client.List(connection, resolvedProjectId); foreach (InfisicalTag tag in tags) { WriteObject(tag); diff --git a/src/PSInfisicalAPI/Cmdlets/InfisicalCmdletBase.cs b/src/PSInfisicalAPI/Cmdlets/InfisicalCmdletBase.cs index fd4f21a..4a3c37b 100644 --- a/src/PSInfisicalAPI/Cmdlets/InfisicalCmdletBase.cs +++ b/src/PSInfisicalAPI/Cmdlets/InfisicalCmdletBase.cs @@ -1,5 +1,6 @@ using System; using System.Management.Automation; +using PSInfisicalAPI.Connections; using PSInfisicalAPI.Errors; using PSInfisicalAPI.Http; using PSInfisicalAPI.Logging; @@ -44,5 +45,43 @@ namespace PSInfisicalAPI.Cmdlets ErrorRecord record = InfisicalErrorHandler.ToErrorRecord(exception, details); ThrowTerminatingError(record); } + + protected string ResolveProjectId(InfisicalConnection connection, string explicitValue) + { + return ResolveValue("ProjectId", explicitValue, connection != null ? connection.ProjectId : null, null); + } + + protected string ResolveEnvironment(InfisicalConnection connection, string explicitValue) + { + return ResolveValue("Environment", explicitValue, connection != null ? connection.Environment : null, null); + } + + protected string ResolveSecretPath(InfisicalConnection connection, string explicitValue) + { + return ResolveValue("SecretPath", explicitValue, connection != null ? connection.DefaultSecretPath : null, "/"); + } + + protected string ResolveApiVersion(InfisicalConnection connection, string explicitValue) + { + string fromConnection = connection != null ? (!string.IsNullOrEmpty(connection.PinnedApiVersion) ? connection.PinnedApiVersion : connection.ApiVersion) : null; + return ResolveValue("ApiVersion", explicitValue, fromConnection, null); + } + + protected string ResolveOrganizationId(InfisicalConnection connection, string explicitValue) + { + return ResolveValue("OrganizationId", explicitValue, connection != null ? connection.OrganizationId : null, null); + } + + private string ResolveValue(string parameterName, string explicitValue, string inheritedValue, string defaultValue) + { + if (!string.IsNullOrEmpty(explicitValue)) { return explicitValue; } + if (!string.IsNullOrEmpty(inheritedValue)) + { + Logger.Verbose(GetType().Name, string.Concat("Inherited ", parameterName, " '", inheritedValue, "' from connection.")); + return inheritedValue; + } + + return defaultValue; + } } } diff --git a/src/PSInfisicalAPI/Cmdlets/NewInfisicalEnvironmentCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/NewInfisicalEnvironmentCmdlet.cs index bcda139..6a00664 100644 --- a/src/PSInfisicalAPI/Cmdlets/NewInfisicalEnvironmentCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/NewInfisicalEnvironmentCmdlet.cs @@ -25,8 +25,9 @@ namespace PSInfisicalAPI.Cmdlets } InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + string resolvedProjectId = ResolveProjectId(connection, ProjectId); InfisicalEnvironmentClient client = new InfisicalEnvironmentClient(HttpClient, Logger); - InfisicalEnvironment env = client.Create(connection, ProjectId, Name, Slug, Position); + InfisicalEnvironment env = client.Create(connection, resolvedProjectId, Name, Slug, Position); if (env != null) { WriteObject(env); diff --git a/src/PSInfisicalAPI/Cmdlets/NewInfisicalFolderCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/NewInfisicalFolderCmdlet.cs index ac1d044..f31ff1d 100644 --- a/src/PSInfisicalAPI/Cmdlets/NewInfisicalFolderCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/NewInfisicalFolderCmdlet.cs @@ -25,8 +25,11 @@ namespace PSInfisicalAPI.Cmdlets } InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + string resolvedProjectId = ResolveProjectId(connection, ProjectId); + string resolvedEnvironment = ResolveEnvironment(connection, Environment); + string resolvedPath = ResolveSecretPath(connection, Path); InfisicalFolderClient client = new InfisicalFolderClient(HttpClient, Logger); - InfisicalFolder folder = client.Create(connection, ProjectId, Environment, Name, Path); + InfisicalFolder folder = client.Create(connection, resolvedProjectId, resolvedEnvironment, Name, resolvedPath); if (folder != null) { WriteObject(folder); diff --git a/src/PSInfisicalAPI/Cmdlets/NewInfisicalSecretCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/NewInfisicalSecretCmdlet.cs index 4dd0e82..2935f2e 100644 --- a/src/PSInfisicalAPI/Cmdlets/NewInfisicalSecretCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/NewInfisicalSecretCmdlet.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Management.Automation; using System.Security; using PSInfisicalAPI.Connections; @@ -12,7 +13,9 @@ namespace PSInfisicalAPI.Cmdlets [OutputType(typeof(InfisicalSecret))] public sealed class NewInfisicalSecretCmdlet : InfisicalCmdletBase { - [Parameter(Mandatory = true, Position = 0)] public string SecretName { get; set; } + [Parameter(Mandatory = true, Position = 0, ParameterSetName = "PlainText")] + [Parameter(Mandatory = true, Position = 0, ParameterSetName = "SecureString")] + public string SecretName { get; set; } [Parameter(Mandatory = true, Position = 1, ParameterSetName = "PlainText")] public string SecretValue { get; set; } @@ -20,6 +23,9 @@ namespace PSInfisicalAPI.Cmdlets [Parameter(Mandatory = true, Position = 1, ParameterSetName = "SecureString")] public SecureString SecureSecretValue { get; set; } + [Parameter(Mandatory = true, Position = 0, ParameterSetName = "Bulk", ValueFromPipeline = true)] + public Hashtable[] Secrets { get; set; } + [Parameter] public string SecretComment { get; set; } [Parameter] public string ProjectId { get; set; } [Parameter] public string Environment { get; set; } @@ -33,35 +39,62 @@ namespace PSInfisicalAPI.Cmdlets { try { - if (!ShouldProcess(SecretName, "Create Infisical secret")) + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + string resolvedProjectId = ResolveProjectId(connection, ProjectId); + string resolvedEnvironment = ResolveEnvironment(connection, Environment); + string resolvedSecretPath = ResolveSecretPath(connection, SecretPath); + string resolvedApiVersion = ResolveApiVersion(connection, ApiVersion); + + if (string.Equals(ParameterSetName, "Bulk", StringComparison.Ordinal)) { + if (Secrets == null || Secrets.Length == 0) { return; } + string target = string.Concat(Secrets.Length, " secret(s)"); + if (!ShouldProcess(target, "Bulk-create Infisical secrets")) { return; } + + InfisicalBulkCreateSecretsRequest bulk = new InfisicalBulkCreateSecretsRequest + { + ProjectId = resolvedProjectId, + Environment = resolvedEnvironment, + SecretPath = resolvedSecretPath, + ApiVersion = resolvedApiVersion, + Secrets = InfisicalBulkSecretConverter.ToCreateItems(Secrets) + }; + + InfisicalSecretsClient bulkClient = new InfisicalSecretsClient(HttpClient, Logger); + InfisicalSecret[] created = bulkClient.CreateBatch(connection, bulk); + if (created != null) + { + foreach (InfisicalSecret secret in created) { WriteObject(secret); } + } + return; } + if (!ShouldProcess(SecretName, "Create Infisical secret")) { return; } + string plainValue = SecureSecretValue != null ? SecureStringUtility.UsePlainText(SecureSecretValue, p => p) : SecretValue; - InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); InfisicalCreateSecretRequest request = new InfisicalCreateSecretRequest { SecretName = SecretName, SecretValue = plainValue, SecretComment = SecretComment, - ProjectId = ProjectId, - Environment = Environment, - SecretPath = SecretPath, + ProjectId = resolvedProjectId, + Environment = resolvedEnvironment, + SecretPath = resolvedSecretPath, Type = Type.ToString(), - ApiVersion = ApiVersion, + ApiVersion = resolvedApiVersion, SkipMultilineEncoding = SkipMultilineEncoding.IsPresent ? (bool?)true : null, TagIds = TagIds }; InfisicalSecretsClient client = new InfisicalSecretsClient(HttpClient, Logger); - InfisicalSecret secret = client.Create(connection, request); - if (secret != null) + InfisicalSecret single = client.Create(connection, request); + if (single != null) { - WriteObject(secret); + WriteObject(single); } } catch (Exception exception) diff --git a/src/PSInfisicalAPI/Cmdlets/NewInfisicalTagCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/NewInfisicalTagCmdlet.cs index 21d99d0..bf26869 100644 --- a/src/PSInfisicalAPI/Cmdlets/NewInfisicalTagCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/NewInfisicalTagCmdlet.cs @@ -25,8 +25,9 @@ namespace PSInfisicalAPI.Cmdlets } InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + string resolvedProjectId = ResolveProjectId(connection, ProjectId); InfisicalTagClient client = new InfisicalTagClient(HttpClient, Logger); - InfisicalTag tag = client.Create(connection, ProjectId, Slug, Name, Color); + InfisicalTag tag = client.Create(connection, resolvedProjectId, Slug, Name, Color); if (tag != null) { WriteObject(tag); diff --git a/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalEnvironmentCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalEnvironmentCmdlet.cs index 54bd9ff..2716bc4 100644 --- a/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalEnvironmentCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalEnvironmentCmdlet.cs @@ -25,8 +25,9 @@ namespace PSInfisicalAPI.Cmdlets } InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + string resolvedProjectId = ResolveProjectId(connection, ProjectId); InfisicalEnvironmentClient client = new InfisicalEnvironmentClient(HttpClient, Logger); - client.Delete(connection, ProjectId, EnvironmentId); + client.Delete(connection, resolvedProjectId, EnvironmentId); if (PassThru.IsPresent) { diff --git a/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalFolderCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalFolderCmdlet.cs index 825bdf3..7dde5d7 100644 --- a/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalFolderCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalFolderCmdlet.cs @@ -27,8 +27,11 @@ namespace PSInfisicalAPI.Cmdlets } InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + string resolvedProjectId = ResolveProjectId(connection, ProjectId); + string resolvedEnvironment = ResolveEnvironment(connection, Environment); + string resolvedPath = ResolveSecretPath(connection, Path); InfisicalFolderClient client = new InfisicalFolderClient(HttpClient, Logger); - client.Delete(connection, ProjectId, Environment, FolderId, Path); + client.Delete(connection, resolvedProjectId, resolvedEnvironment, FolderId, resolvedPath); if (PassThru.IsPresent) { diff --git a/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalProjectCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalProjectCmdlet.cs index 6cd89e1..fbab178 100644 --- a/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalProjectCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalProjectCmdlet.cs @@ -8,7 +8,7 @@ namespace PSInfisicalAPI.Cmdlets [Cmdlet(VerbsCommon.Remove, "InfisicalProject", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)] public sealed class RemoveInfisicalProjectCmdlet : InfisicalCmdletBase { - [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)] + [Parameter(ValueFromPipelineByPropertyName = true, Position = 0)] [Alias("Id")] public string ProjectId { get; set; } @@ -18,18 +18,20 @@ namespace PSInfisicalAPI.Cmdlets { try { - if (!ShouldProcess(ProjectId, "Remove Infisical project")) + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + string resolvedProjectId = ResolveProjectId(connection, ProjectId); + + if (!ShouldProcess(resolvedProjectId, "Remove Infisical project")) { return; } - InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); InfisicalProjectClient client = new InfisicalProjectClient(HttpClient, Logger); - client.Delete(connection, ProjectId); + client.Delete(connection, resolvedProjectId); if (PassThru.IsPresent) { - WriteObject(ProjectId); + WriteObject(resolvedProjectId); } } catch (Exception exception) diff --git a/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalSecretCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalSecretCmdlet.cs index 3087596..d2154c7 100644 --- a/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalSecretCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalSecretCmdlet.cs @@ -6,12 +6,16 @@ using PSInfisicalAPI.Secrets; namespace PSInfisicalAPI.Cmdlets { - [Cmdlet(VerbsCommon.Remove, "InfisicalSecret", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)] + [Cmdlet(VerbsCommon.Remove, "InfisicalSecret", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High, DefaultParameterSetName = "Single")] public sealed class RemoveInfisicalSecretCmdlet : InfisicalCmdletBase { - [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)] + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0, ParameterSetName = "Single")] public string SecretName { get; set; } + [Parameter(Mandatory = true, Position = 0, ParameterSetName = "Bulk", ValueFromPipeline = true)] + [Alias("Names", "SecretKeys")] + public string[] SecretNames { get; set; } + [Parameter] public string ProjectId { get; set; } [Parameter] public string Environment { get; set; } [Parameter] public string SecretPath { get; set; } @@ -23,23 +27,51 @@ namespace PSInfisicalAPI.Cmdlets { try { - if (!ShouldProcess(SecretName, "Remove Infisical secret")) + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + string resolvedProjectId = ResolveProjectId(connection, ProjectId); + string resolvedEnvironment = ResolveEnvironment(connection, Environment); + string resolvedSecretPath = ResolveSecretPath(connection, SecretPath); + string resolvedApiVersion = ResolveApiVersion(connection, ApiVersion); + + InfisicalSecretsClient client = new InfisicalSecretsClient(HttpClient, Logger); + + if (string.Equals(ParameterSetName, "Bulk", StringComparison.Ordinal)) { + if (SecretNames == null || SecretNames.Length == 0) { return; } + string target = string.Concat(SecretNames.Length, " secret(s)"); + if (!ShouldProcess(target, "Bulk-remove Infisical secrets")) { return; } + + InfisicalBulkDeleteSecretsRequest bulk = new InfisicalBulkDeleteSecretsRequest + { + ProjectId = resolvedProjectId, + Environment = resolvedEnvironment, + SecretPath = resolvedSecretPath, + ApiVersion = resolvedApiVersion, + SecretNames = SecretNames + }; + + client.DeleteBatch(connection, bulk); + + if (PassThru.IsPresent) + { + foreach (string name in SecretNames) { WriteObject(name); } + } + return; } - InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + if (!ShouldProcess(SecretName, "Remove Infisical secret")) { return; } + InfisicalDeleteSecretRequest request = new InfisicalDeleteSecretRequest { SecretName = SecretName, - ProjectId = ProjectId, - Environment = Environment, - SecretPath = SecretPath, + ProjectId = resolvedProjectId, + Environment = resolvedEnvironment, + SecretPath = resolvedSecretPath, Type = Type.ToString(), - ApiVersion = ApiVersion + ApiVersion = resolvedApiVersion }; - InfisicalSecretsClient client = new InfisicalSecretsClient(HttpClient, Logger); client.Delete(connection, request); if (PassThru.IsPresent) diff --git a/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalTagCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalTagCmdlet.cs index bb14432..96b3b7e 100644 --- a/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalTagCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/RemoveInfisicalTagCmdlet.cs @@ -25,8 +25,9 @@ namespace PSInfisicalAPI.Cmdlets } InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + string resolvedProjectId = ResolveProjectId(connection, ProjectId); InfisicalTagClient client = new InfisicalTagClient(HttpClient, Logger); - client.Delete(connection, ProjectId, TagId); + client.Delete(connection, resolvedProjectId, TagId); if (PassThru.IsPresent) { diff --git a/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalEnvironmentCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalEnvironmentCmdlet.cs index ade169e..76de675 100644 --- a/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalEnvironmentCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalEnvironmentCmdlet.cs @@ -29,8 +29,9 @@ namespace PSInfisicalAPI.Cmdlets } InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + string resolvedProjectId = ResolveProjectId(connection, ProjectId); InfisicalEnvironmentClient client = new InfisicalEnvironmentClient(HttpClient, Logger); - InfisicalEnvironment env = client.Update(connection, ProjectId, EnvironmentId, Name, Slug, Position); + InfisicalEnvironment env = client.Update(connection, resolvedProjectId, EnvironmentId, Name, Slug, Position); if (env != null) { WriteObject(env); diff --git a/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalFolderCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalFolderCmdlet.cs index 891b398..bb5fe36 100644 --- a/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalFolderCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalFolderCmdlet.cs @@ -29,8 +29,11 @@ namespace PSInfisicalAPI.Cmdlets } InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + string resolvedProjectId = ResolveProjectId(connection, ProjectId); + string resolvedEnvironment = ResolveEnvironment(connection, Environment); + string resolvedPath = ResolveSecretPath(connection, Path); InfisicalFolderClient client = new InfisicalFolderClient(HttpClient, Logger); - InfisicalFolder folder = client.Update(connection, ProjectId, Environment, FolderId, Name, Path); + InfisicalFolder folder = client.Update(connection, resolvedProjectId, resolvedEnvironment, FolderId, Name, resolvedPath); if (folder != null) { WriteObject(folder); diff --git a/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalProjectCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalProjectCmdlet.cs index 3a58b3f..a76cb6b 100644 --- a/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalProjectCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalProjectCmdlet.cs @@ -10,7 +10,7 @@ namespace PSInfisicalAPI.Cmdlets [OutputType(typeof(InfisicalProject))] public sealed class UpdateInfisicalProjectCmdlet : InfisicalCmdletBase { - [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)] + [Parameter(ValueFromPipelineByPropertyName = true, Position = 0)] [Alias("Id")] public string ProjectId { get; set; } @@ -22,14 +22,16 @@ namespace PSInfisicalAPI.Cmdlets { try { - if (!ShouldProcess(ProjectId, "Update Infisical project")) + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + string resolvedProjectId = ResolveProjectId(connection, ProjectId); + + if (!ShouldProcess(resolvedProjectId, "Update Infisical project")) { return; } - InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); InfisicalProjectClient client = new InfisicalProjectClient(HttpClient, Logger); - InfisicalProject project = client.Update(connection, ProjectId, Name, Description, AutoCapitalization); + InfisicalProject project = client.Update(connection, resolvedProjectId, Name, Description, AutoCapitalization); if (project != null) { WriteObject(project); diff --git a/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalSecretCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalSecretCmdlet.cs index 359025f..0bb6912 100644 --- a/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalSecretCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalSecretCmdlet.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Management.Automation; using System.Security; using PSInfisicalAPI.Connections; @@ -12,12 +13,16 @@ namespace PSInfisicalAPI.Cmdlets [OutputType(typeof(InfisicalSecret))] public sealed class UpdateInfisicalSecretCmdlet : InfisicalCmdletBase { - [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)] + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0, ParameterSetName = "PlainText")] + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0, ParameterSetName = "SecureString")] public string SecretName { get; set; } [Parameter(ParameterSetName = "PlainText")] public string SecretValue { get; set; } [Parameter(ParameterSetName = "SecureString")] public SecureString SecureSecretValue { get; set; } + [Parameter(Mandatory = true, Position = 0, ParameterSetName = "Bulk", ValueFromPipeline = true)] + public Hashtable[] Secrets { get; set; } + [Parameter] public string NewSecretName { get; set; } [Parameter] public string SecretComment { get; set; } [Parameter] public string ProjectId { get; set; } @@ -32,36 +37,63 @@ namespace PSInfisicalAPI.Cmdlets { try { - if (!ShouldProcess(SecretName, "Update Infisical secret")) + InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + string resolvedProjectId = ResolveProjectId(connection, ProjectId); + string resolvedEnvironment = ResolveEnvironment(connection, Environment); + string resolvedSecretPath = ResolveSecretPath(connection, SecretPath); + string resolvedApiVersion = ResolveApiVersion(connection, ApiVersion); + + if (string.Equals(ParameterSetName, "Bulk", StringComparison.Ordinal)) { + if (Secrets == null || Secrets.Length == 0) { return; } + string target = string.Concat(Secrets.Length, " secret(s)"); + if (!ShouldProcess(target, "Bulk-update Infisical secrets")) { return; } + + InfisicalBulkUpdateSecretsRequest bulk = new InfisicalBulkUpdateSecretsRequest + { + ProjectId = resolvedProjectId, + Environment = resolvedEnvironment, + SecretPath = resolvedSecretPath, + ApiVersion = resolvedApiVersion, + Secrets = InfisicalBulkSecretConverter.ToUpdateItems(Secrets) + }; + + InfisicalSecretsClient bulkClient = new InfisicalSecretsClient(HttpClient, Logger); + InfisicalSecret[] updated = bulkClient.UpdateBatch(connection, bulk); + if (updated != null) + { + foreach (InfisicalSecret secret in updated) { WriteObject(secret); } + } + return; } + if (!ShouldProcess(SecretName, "Update Infisical secret")) { return; } + string plainValue = SecureSecretValue != null ? SecureStringUtility.UsePlainText(SecureSecretValue, p => p) : SecretValue; - InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); InfisicalUpdateSecretRequest request = new InfisicalUpdateSecretRequest { SecretName = SecretName, NewSecretName = NewSecretName, SecretValue = plainValue, SecretComment = SecretComment, - ProjectId = ProjectId, - Environment = Environment, - SecretPath = SecretPath, + ProjectId = resolvedProjectId, + Environment = resolvedEnvironment, + SecretPath = resolvedSecretPath, Type = Type.ToString(), - ApiVersion = ApiVersion, + ApiVersion = resolvedApiVersion, SkipMultilineEncoding = SkipMultilineEncoding.IsPresent ? (bool?)true : null, TagIds = TagIds }; InfisicalSecretsClient client = new InfisicalSecretsClient(HttpClient, Logger); - InfisicalSecret secret = client.Update(connection, request); - if (secret != null) + InfisicalSecret single = client.Update(connection, request); + if (single != null) { - WriteObject(secret); + WriteObject(single); } } catch (Exception exception) diff --git a/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalTagCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalTagCmdlet.cs index 9a6c45b..15aefe6 100644 --- a/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalTagCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalTagCmdlet.cs @@ -29,8 +29,9 @@ namespace PSInfisicalAPI.Cmdlets } InfisicalConnection connection = InfisicalSessionManager.RequireCurrent(); + string resolvedProjectId = ResolveProjectId(connection, ProjectId); InfisicalTagClient client = new InfisicalTagClient(HttpClient, Logger); - InfisicalTag tag = client.Update(connection, ProjectId, TagId, Slug, Name, Color); + InfisicalTag tag = client.Update(connection, resolvedProjectId, TagId, Slug, Name, Color); if (tag != null) { WriteObject(tag); diff --git a/src/PSInfisicalAPI/Endpoints/InfisicalEndpointNames.cs b/src/PSInfisicalAPI/Endpoints/InfisicalEndpointNames.cs index 83a8d0e..161c3a5 100644 --- a/src/PSInfisicalAPI/Endpoints/InfisicalEndpointNames.cs +++ b/src/PSInfisicalAPI/Endpoints/InfisicalEndpointNames.cs @@ -15,6 +15,10 @@ namespace PSInfisicalAPI.Endpoints public const string CreateSecret = "CreateSecret"; public const string UpdateSecret = "UpdateSecret"; public const string DeleteSecret = "DeleteSecret"; + public const string BulkCreateSecret = "BulkCreateSecret"; + public const string BulkUpdateSecret = "BulkUpdateSecret"; + public const string BulkDeleteSecret = "BulkDeleteSecret"; + public const string DuplicateSecret = "DuplicateSecret"; public const string ListProjects = "ListProjects"; public const string RetrieveProject = "RetrieveProject"; diff --git a/src/PSInfisicalAPI/Endpoints/InfisicalEndpointRegistry.cs b/src/PSInfisicalAPI/Endpoints/InfisicalEndpointRegistry.cs index 0478a5c..332de5a 100644 --- a/src/PSInfisicalAPI/Endpoints/InfisicalEndpointRegistry.cs +++ b/src/PSInfisicalAPI/Endpoints/InfisicalEndpointRegistry.cs @@ -197,6 +197,54 @@ namespace PSInfisicalAPI.Endpoints RequiresAuthorization = true, ContainsSecretMaterialInResponse = true }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.BulkCreateSecret, + Resource = "Secrets", + Version = "v3", + Method = "POST", + Template = "/api/v3/secrets/batch/raw", + RequiresAuthorization = true, + ContainsSecretMaterialInRequest = true, + ContainsSecretMaterialInResponse = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.BulkUpdateSecret, + Resource = "Secrets", + Version = "v3", + Method = "PATCH", + Template = "/api/v3/secrets/batch/raw", + RequiresAuthorization = true, + ContainsSecretMaterialInRequest = true, + ContainsSecretMaterialInResponse = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.BulkDeleteSecret, + Resource = "Secrets", + Version = "v3", + Method = "DELETE", + Template = "/api/v3/secrets/batch/raw", + RequiresAuthorization = true, + ContainsSecretMaterialInRequest = true, + ContainsSecretMaterialInResponse = true + }); + + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.DuplicateSecret, + Resource = "Secrets", + Version = "v4", + Method = "POST", + Template = "/api/v4/secrets/duplicate", + RequiresAuthorization = true, + ContainsSecretMaterialInRequest = true, + ContainsSecretMaterialInResponse = true + }); } private static void RegisterProjects(Dictionary> map) diff --git a/src/PSInfisicalAPI/Secrets/InfisicalBulkSecretConverter.cs b/src/PSInfisicalAPI/Secrets/InfisicalBulkSecretConverter.cs new file mode 100644 index 0000000..ba4f37d --- /dev/null +++ b/src/PSInfisicalAPI/Secrets/InfisicalBulkSecretConverter.cs @@ -0,0 +1,164 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using PSInfisicalAPI.Errors; +using PSInfisicalAPI.Security; + +namespace PSInfisicalAPI.Secrets +{ + public static class InfisicalBulkSecretConverter + { + public static InfisicalBulkCreateSecretItem[] ToCreateItems(IEnumerable input) + { + if (input == null) { return new InfisicalBulkCreateSecretItem[0]; } + + List list = new List(); + foreach (object element in input) + { + Hashtable table = AsHashtable(element); + InfisicalBulkCreateSecretItem item = new InfisicalBulkCreateSecretItem + { + SecretName = GetString(table, "SecretName", "Name", "Key", "SecretKey"), + SecretValue = GetSecretValue(table, "SecretValue", "Value"), + SecretComment = GetString(table, "SecretComment", "Comment"), + SkipMultilineEncoding = GetBool(table, "SkipMultilineEncoding"), + TagIds = GetStringArray(table, "TagIds"), + SecretMetadata = GetStringDictionary(table, "SecretMetadata", "Metadata") + }; + + if (string.IsNullOrEmpty(item.SecretName)) + { + throw new InfisicalConfigurationException("Each bulk-create entry must include 'SecretName' (or 'Name'/'Key')."); + } + + list.Add(item); + } + + return list.ToArray(); + } + + public static InfisicalBulkUpdateSecretItem[] ToUpdateItems(IEnumerable input) + { + if (input == null) { return new InfisicalBulkUpdateSecretItem[0]; } + + List list = new List(); + foreach (object element in input) + { + Hashtable table = AsHashtable(element); + InfisicalBulkUpdateSecretItem item = new InfisicalBulkUpdateSecretItem + { + SecretName = GetString(table, "SecretName", "Name", "Key", "SecretKey"), + NewSecretName = GetString(table, "NewSecretName", "NewName"), + SecretValue = GetSecretValue(table, "SecretValue", "Value"), + SecretComment = GetString(table, "SecretComment", "Comment"), + SkipMultilineEncoding = GetBool(table, "SkipMultilineEncoding"), + TagIds = GetStringArray(table, "TagIds"), + SecretMetadata = GetStringDictionary(table, "SecretMetadata", "Metadata") + }; + + if (string.IsNullOrEmpty(item.SecretName)) + { + throw new InfisicalConfigurationException("Each bulk-update entry must include 'SecretName' (or 'Name'/'Key')."); + } + + list.Add(item); + } + + return list.ToArray(); + } + + private static Hashtable AsHashtable(object element) + { + if (element is Hashtable hashtable) { return hashtable; } + if (element is IDictionary dictionary) + { + Hashtable converted = new Hashtable(StringComparer.OrdinalIgnoreCase); + foreach (DictionaryEntry entry in dictionary) + { + if (entry.Key == null) { continue; } + converted[entry.Key.ToString()] = entry.Value; + } + + return converted; + } + + throw new InfisicalConfigurationException("Bulk secret entries must be Hashtable or IDictionary values."); + } + + private static string GetString(Hashtable table, params string[] keys) + { + foreach (string key in keys) + { + if (table.ContainsKey(key) && table[key] != null) + { + return table[key].ToString(); + } + } + + return null; + } + + private static string GetSecretValue(Hashtable table, params string[] keys) + { + foreach (string key in keys) + { + if (!table.ContainsKey(key)) { continue; } + object value = table[key]; + if (value == null) { return null; } + if (value is System.Security.SecureString secure) + { + return SecureStringUtility.UsePlainText(secure, plain => plain); + } + + return value.ToString(); + } + + return null; + } + + private static bool? GetBool(Hashtable table, string key) + { + if (!table.ContainsKey(key) || table[key] == null) { return null; } + object value = table[key]; + if (value is bool b) { return b; } + bool parsed; + return bool.TryParse(value.ToString(), out parsed) ? parsed : (bool?)null; + } + + private static string[] GetStringArray(Hashtable table, string key) + { + if (!table.ContainsKey(key) || table[key] == null) { return null; } + object value = table[key]; + if (value is string[] direct) { return direct; } + if (value is IEnumerable enumerable && !(value is string)) + { + List items = new List(); + foreach (object item in enumerable) { if (item != null) { items.Add(item.ToString()); } } + return items.ToArray(); + } + + return new[] { value.ToString() }; + } + + private static Dictionary GetStringDictionary(Hashtable table, params string[] keys) + { + foreach (string key in keys) + { + if (!table.ContainsKey(key) || table[key] == null) { continue; } + if (table[key] is IDictionary dictionary) + { + Dictionary result = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (DictionaryEntry entry in dictionary) + { + if (entry.Key == null) { continue; } + result[entry.Key.ToString()] = entry.Value != null ? entry.Value.ToString() : null; + } + + return result; + } + } + + return null; + } + } +} diff --git a/src/PSInfisicalAPI/Secrets/InfisicalSecretDtos.cs b/src/PSInfisicalAPI/Secrets/InfisicalSecretDtos.cs index 8d4ea3a..d4dd9cc 100644 --- a/src/PSInfisicalAPI/Secrets/InfisicalSecretDtos.cs +++ b/src/PSInfisicalAPI/Secrets/InfisicalSecretDtos.cs @@ -88,4 +88,75 @@ namespace PSInfisicalAPI.Secrets [JsonProperty("secretPath", NullValueHandling = NullValueHandling.Ignore)] public string SecretPath { get; set; } [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)] public string Type { get; set; } } + + internal sealed class InfisicalSecretBatchCreateItemDto + { + [JsonProperty("secretKey")] public string SecretKey { get; set; } + [JsonProperty("secretValue")] public string SecretValue { get; set; } + [JsonProperty("secretComment", NullValueHandling = NullValueHandling.Ignore)] public string SecretComment { get; set; } + [JsonProperty("skipMultilineEncoding", NullValueHandling = NullValueHandling.Ignore)] public bool? SkipMultilineEncoding { get; set; } + [JsonProperty("tagIds", NullValueHandling = NullValueHandling.Ignore)] public string[] TagIds { get; set; } + [JsonProperty("secretMetadata", NullValueHandling = NullValueHandling.Ignore)] public List SecretMetadata { get; set; } + } + + internal sealed class InfisicalSecretBatchUpdateItemDto + { + [JsonProperty("secretKey")] public string SecretKey { get; set; } + [JsonProperty("newSecretName", NullValueHandling = NullValueHandling.Ignore)] public string NewSecretName { get; set; } + [JsonProperty("secretValue", NullValueHandling = NullValueHandling.Ignore)] public string SecretValue { get; set; } + [JsonProperty("secretComment", NullValueHandling = NullValueHandling.Ignore)] public string SecretComment { get; set; } + [JsonProperty("skipMultilineEncoding", NullValueHandling = NullValueHandling.Ignore)] public bool? SkipMultilineEncoding { get; set; } + [JsonProperty("tagIds", NullValueHandling = NullValueHandling.Ignore)] public string[] TagIds { get; set; } + [JsonProperty("secretMetadata", NullValueHandling = NullValueHandling.Ignore)] public List SecretMetadata { get; set; } + } + + internal sealed class InfisicalSecretBatchDeleteItemDto + { + [JsonProperty("secretKey")] public string SecretKey { get; set; } + } + + internal sealed class InfisicalSecretBatchCreateRequestDto + { + [JsonProperty("workspaceId")] public string WorkspaceId { get; set; } + [JsonProperty("environment")] public string Environment { get; set; } + [JsonProperty("secretPath", NullValueHandling = NullValueHandling.Ignore)] public string SecretPath { get; set; } + [JsonProperty("secrets")] public List Secrets { get; set; } + } + + internal sealed class InfisicalSecretBatchUpdateRequestDto + { + [JsonProperty("workspaceId")] public string WorkspaceId { get; set; } + [JsonProperty("environment")] public string Environment { get; set; } + [JsonProperty("secretPath", NullValueHandling = NullValueHandling.Ignore)] public string SecretPath { get; set; } + [JsonProperty("mode", NullValueHandling = NullValueHandling.Ignore)] public string Mode { get; set; } + [JsonProperty("secrets")] public List Secrets { get; set; } + } + + internal sealed class InfisicalSecretBatchDeleteRequestDto + { + [JsonProperty("workspaceId")] public string WorkspaceId { get; set; } + [JsonProperty("environment")] public string Environment { get; set; } + [JsonProperty("secretPath", NullValueHandling = NullValueHandling.Ignore)] public string SecretPath { get; set; } + [JsonProperty("secrets")] public List Secrets { get; set; } + } + + internal sealed class InfisicalSecretDuplicateAttributesDto + { + [JsonProperty("secretValue", NullValueHandling = NullValueHandling.Ignore)] public bool? SecretValue { get; set; } + [JsonProperty("secretComment", NullValueHandling = NullValueHandling.Ignore)] public bool? SecretComment { get; set; } + [JsonProperty("tags", NullValueHandling = NullValueHandling.Ignore)] public bool? Tags { get; set; } + [JsonProperty("metadata", NullValueHandling = NullValueHandling.Ignore)] public bool? Metadata { get; set; } + } + + internal sealed class InfisicalSecretDuplicateRequestDto + { + [JsonProperty("projectId")] public string ProjectId { get; set; } + [JsonProperty("sourceEnvironment")] public string SourceEnvironment { get; set; } + [JsonProperty("destinationEnvironment")] public string DestinationEnvironment { get; set; } + [JsonProperty("sourceSecretPath", NullValueHandling = NullValueHandling.Ignore)] public string SourceSecretPath { get; set; } + [JsonProperty("destinationSecretPath", NullValueHandling = NullValueHandling.Ignore)] public string DestinationSecretPath { get; set; } + [JsonProperty("secretIds")] public string[] SecretIds { get; set; } + [JsonProperty("overwriteExisting", NullValueHandling = NullValueHandling.Ignore)] public bool? OverwriteExisting { get; set; } + [JsonProperty("attributesToCopy", NullValueHandling = NullValueHandling.Ignore)] public InfisicalSecretDuplicateAttributesDto AttributesToCopy { get; set; } + } } diff --git a/src/PSInfisicalAPI/Secrets/InfisicalSecretQuery.cs b/src/PSInfisicalAPI/Secrets/InfisicalSecretQuery.cs index a5e20d1..0a274d5 100644 --- a/src/PSInfisicalAPI/Secrets/InfisicalSecretQuery.cs +++ b/src/PSInfisicalAPI/Secrets/InfisicalSecretQuery.cs @@ -69,4 +69,69 @@ namespace PSInfisicalAPI.Secrets public string Type { get; set; } public string ApiVersion { get; set; } } + + public sealed class InfisicalBulkCreateSecretItem + { + public string SecretName { get; set; } + public string SecretValue { get; set; } + public string SecretComment { get; set; } + public bool? SkipMultilineEncoding { get; set; } + public string[] TagIds { get; set; } + public Dictionary SecretMetadata { get; set; } + } + + public sealed class InfisicalBulkUpdateSecretItem + { + public string SecretName { get; set; } + public string NewSecretName { get; set; } + public string SecretValue { get; set; } + public string SecretComment { get; set; } + public bool? SkipMultilineEncoding { get; set; } + public string[] TagIds { get; set; } + public Dictionary SecretMetadata { get; set; } + } + + public sealed class InfisicalBulkCreateSecretsRequest + { + public string ProjectId { get; set; } + public string Environment { get; set; } + public string SecretPath { get; set; } + public string ApiVersion { get; set; } + public InfisicalBulkCreateSecretItem[] Secrets { get; set; } + } + + public sealed class InfisicalBulkUpdateSecretsRequest + { + public string ProjectId { get; set; } + public string Environment { get; set; } + public string SecretPath { get; set; } + public string ApiVersion { get; set; } + public string Mode { get; set; } + public InfisicalBulkUpdateSecretItem[] Secrets { get; set; } + } + + public sealed class InfisicalBulkDeleteSecretsRequest + { + public string ProjectId { get; set; } + public string Environment { get; set; } + public string SecretPath { get; set; } + public string ApiVersion { get; set; } + public string[] SecretNames { get; set; } + } + + public sealed class InfisicalDuplicateSecretsRequest + { + public string ProjectId { get; set; } + public string SourceEnvironment { get; set; } + public string DestinationEnvironment { get; set; } + public string SourceSecretPath { get; set; } + public string DestinationSecretPath { get; set; } + public string[] SecretIds { get; set; } + public bool? OverwriteExisting { get; set; } + public bool? CopySecretValue { get; set; } + public bool? CopySecretComment { get; set; } + public bool? CopyTags { get; set; } + public bool? CopyMetadata { get; set; } + public string ApiVersion { get; set; } + } } diff --git a/src/PSInfisicalAPI/Secrets/InfisicalSecretsClient.cs b/src/PSInfisicalAPI/Secrets/InfisicalSecretsClient.cs index 2f62246..9d7e93a 100644 --- a/src/PSInfisicalAPI/Secrets/InfisicalSecretsClient.cs +++ b/src/PSInfisicalAPI/Secrets/InfisicalSecretsClient.cs @@ -224,6 +224,227 @@ namespace PSInfisicalAPI.Secrets } } + public InfisicalSecret[] CreateBatch(InfisicalConnection connection, InfisicalBulkCreateSecretsRequest request) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + if (request == null) { throw new ArgumentNullException(nameof(request)); } + if (request.Secrets == null || request.Secrets.Length == 0) { throw new InfisicalConfigurationException("At least one secret is required."); } + + string resolvedProjectId = FirstNonEmpty(request.ProjectId, connection.ProjectId); + string resolvedEnvironment = FirstNonEmpty(request.Environment, connection.Environment); + if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); } + if (string.IsNullOrEmpty(resolvedEnvironment)) { throw new InfisicalConfigurationException("Environment is required."); } + + List items = new List(request.Secrets.Length); + foreach (InfisicalBulkCreateSecretItem item in request.Secrets) + { + if (item == null) { continue; } + if (string.IsNullOrEmpty(item.SecretName)) { throw new InfisicalConfigurationException("Each bulk-create item requires SecretName."); } + items.Add(new InfisicalSecretBatchCreateItemDto + { + SecretKey = item.SecretName, + SecretValue = item.SecretValue ?? string.Empty, + SecretComment = item.SecretComment, + SkipMultilineEncoding = item.SkipMultilineEncoding, + TagIds = item.TagIds, + SecretMetadata = ToMetadataDtoList(item.SecretMetadata) + }); + } + + InfisicalSecretBatchCreateRequestDto dtoRequest = new InfisicalSecretBatchCreateRequestDto + { + WorkspaceId = resolvedProjectId, + Environment = resolvedEnvironment, + SecretPath = FirstNonEmpty(request.SecretPath, connection.DefaultSecretPath, "/"), + Secrets = items + }; + string body = _serializer.Serialize(dtoRequest); + + try + { + _logger.Information(Component, string.Concat("Attempting to bulk-create ", items.Count.ToString(CultureInfo.InvariantCulture), " Infisical secret(s). Please Wait...")); + InfisicalHttpResponse response = SendWithVersionFallback(connection, InfisicalEndpointNames.BulkCreateSecret, request.ApiVersion, "BulkCreateSecrets", null, null, body); + InfisicalSecretListResponseDto dto = _serializer.Deserialize(response.Body); + response.Clear(); + + InfisicalSecret[] mapped = InfisicalSecretMapper.MapMany(dto != null ? dto.Secrets : null); + _logger.Information(Component, "Infisical bulk secret creation was successful."); + return mapped; + } + catch (Exception) + { + _logger.Error(Component, "Infisical bulk secret creation failed."); + throw; + } + } + + public InfisicalSecret[] UpdateBatch(InfisicalConnection connection, InfisicalBulkUpdateSecretsRequest request) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + if (request == null) { throw new ArgumentNullException(nameof(request)); } + if (request.Secrets == null || request.Secrets.Length == 0) { throw new InfisicalConfigurationException("At least one secret is required."); } + + string resolvedProjectId = FirstNonEmpty(request.ProjectId, connection.ProjectId); + string resolvedEnvironment = FirstNonEmpty(request.Environment, connection.Environment); + if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); } + if (string.IsNullOrEmpty(resolvedEnvironment)) { throw new InfisicalConfigurationException("Environment is required."); } + + List items = new List(request.Secrets.Length); + foreach (InfisicalBulkUpdateSecretItem item in request.Secrets) + { + if (item == null) { continue; } + if (string.IsNullOrEmpty(item.SecretName)) { throw new InfisicalConfigurationException("Each bulk-update item requires SecretName."); } + items.Add(new InfisicalSecretBatchUpdateItemDto + { + SecretKey = item.SecretName, + NewSecretName = item.NewSecretName, + SecretValue = item.SecretValue, + SecretComment = item.SecretComment, + SkipMultilineEncoding = item.SkipMultilineEncoding, + TagIds = item.TagIds, + SecretMetadata = ToMetadataDtoList(item.SecretMetadata) + }); + } + + InfisicalSecretBatchUpdateRequestDto dtoRequest = new InfisicalSecretBatchUpdateRequestDto + { + WorkspaceId = resolvedProjectId, + Environment = resolvedEnvironment, + SecretPath = FirstNonEmpty(request.SecretPath, connection.DefaultSecretPath, "/"), + Mode = request.Mode, + Secrets = items + }; + string body = _serializer.Serialize(dtoRequest); + + try + { + _logger.Information(Component, string.Concat("Attempting to bulk-update ", items.Count.ToString(CultureInfo.InvariantCulture), " Infisical secret(s). Please Wait...")); + InfisicalHttpResponse response = SendWithVersionFallback(connection, InfisicalEndpointNames.BulkUpdateSecret, request.ApiVersion, "BulkUpdateSecrets", null, null, body); + InfisicalSecretListResponseDto dto = _serializer.Deserialize(response.Body); + response.Clear(); + + InfisicalSecret[] mapped = InfisicalSecretMapper.MapMany(dto != null ? dto.Secrets : null); + _logger.Information(Component, "Infisical bulk secret update was successful."); + return mapped; + } + catch (Exception) + { + _logger.Error(Component, "Infisical bulk secret update failed."); + throw; + } + } + + public void DeleteBatch(InfisicalConnection connection, InfisicalBulkDeleteSecretsRequest request) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + if (request == null) { throw new ArgumentNullException(nameof(request)); } + if (request.SecretNames == null || request.SecretNames.Length == 0) { throw new InfisicalConfigurationException("At least one secret name is required."); } + + string resolvedProjectId = FirstNonEmpty(request.ProjectId, connection.ProjectId); + string resolvedEnvironment = FirstNonEmpty(request.Environment, connection.Environment); + if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); } + if (string.IsNullOrEmpty(resolvedEnvironment)) { throw new InfisicalConfigurationException("Environment is required."); } + + List items = new List(request.SecretNames.Length); + foreach (string name in request.SecretNames) + { + if (string.IsNullOrEmpty(name)) { continue; } + items.Add(new InfisicalSecretBatchDeleteItemDto { SecretKey = name }); + } + + InfisicalSecretBatchDeleteRequestDto dtoRequest = new InfisicalSecretBatchDeleteRequestDto + { + WorkspaceId = resolvedProjectId, + Environment = resolvedEnvironment, + SecretPath = FirstNonEmpty(request.SecretPath, connection.DefaultSecretPath, "/"), + Secrets = items + }; + string body = _serializer.Serialize(dtoRequest); + + try + { + _logger.Information(Component, string.Concat("Attempting to bulk-delete ", items.Count.ToString(CultureInfo.InvariantCulture), " Infisical secret(s). Please Wait...")); + InfisicalHttpResponse response = SendWithVersionFallback(connection, InfisicalEndpointNames.BulkDeleteSecret, request.ApiVersion, "BulkDeleteSecrets", null, null, body); + response.Clear(); + _logger.Information(Component, "Infisical bulk secret deletion was successful."); + } + catch (Exception) + { + _logger.Error(Component, "Infisical bulk secret deletion failed."); + throw; + } + } + + public InfisicalSecret[] Duplicate(InfisicalConnection connection, InfisicalDuplicateSecretsRequest request) + { + if (connection == null) { throw new ArgumentNullException(nameof(connection)); } + if (request == null) { throw new ArgumentNullException(nameof(request)); } + if (request.SecretIds == null || request.SecretIds.Length == 0) { throw new InfisicalConfigurationException("At least one SecretId is required."); } + + string resolvedProjectId = FirstNonEmpty(request.ProjectId, connection.ProjectId); + string resolvedSourceEnv = FirstNonEmpty(request.SourceEnvironment, connection.Environment); + if (string.IsNullOrEmpty(resolvedProjectId)) { throw new InfisicalConfigurationException("ProjectId is required."); } + if (string.IsNullOrEmpty(resolvedSourceEnv)) { throw new InfisicalConfigurationException("SourceEnvironment is required."); } + if (string.IsNullOrEmpty(request.DestinationEnvironment)) { throw new InfisicalConfigurationException("DestinationEnvironment is required."); } + + string resolvedSourcePath = FirstNonEmpty(request.SourceSecretPath, connection.DefaultSecretPath, "/"); + string resolvedDestPath = FirstNonEmpty(request.DestinationSecretPath, resolvedSourcePath); + + InfisicalSecretDuplicateAttributesDto attributes = null; + if (request.CopySecretValue.HasValue || request.CopySecretComment.HasValue || request.CopyTags.HasValue || request.CopyMetadata.HasValue) + { + attributes = new InfisicalSecretDuplicateAttributesDto + { + SecretValue = request.CopySecretValue, + SecretComment = request.CopySecretComment, + Tags = request.CopyTags, + Metadata = request.CopyMetadata + }; + } + + InfisicalSecretDuplicateRequestDto dtoRequest = new InfisicalSecretDuplicateRequestDto + { + ProjectId = resolvedProjectId, + SourceEnvironment = resolvedSourceEnv, + DestinationEnvironment = request.DestinationEnvironment, + SourceSecretPath = resolvedSourcePath, + DestinationSecretPath = resolvedDestPath, + SecretIds = request.SecretIds, + OverwriteExisting = request.OverwriteExisting, + AttributesToCopy = attributes + }; + string body = _serializer.Serialize(dtoRequest); + + try + { + _logger.Information(Component, string.Concat("Attempting to duplicate ", request.SecretIds.Length.ToString(CultureInfo.InvariantCulture), " Infisical secret(s). Please Wait...")); + InfisicalHttpResponse response = SendWithVersionFallback(connection, InfisicalEndpointNames.DuplicateSecret, request.ApiVersion, "DuplicateSecrets", null, null, body); + InfisicalSecretListResponseDto dto = _serializer.Deserialize(response.Body); + response.Clear(); + + InfisicalSecret[] mapped = InfisicalSecretMapper.MapMany(dto != null ? dto.Secrets : null); + _logger.Information(Component, "Infisical secret duplication was successful."); + return mapped; + } + catch (Exception) + { + _logger.Error(Component, "Infisical secret duplication failed."); + throw; + } + } + + private static List ToMetadataDtoList(Dictionary metadata) + { + if (metadata == null || metadata.Count == 0) { return null; } + List list = new List(metadata.Count); + foreach (KeyValuePair kvp in metadata) + { + list.Add(new InfisicalSecretMetadataDto { Key = kvp.Key, Value = kvp.Value }); + } + + return list; + } + public void Delete(InfisicalConnection connection, InfisicalDeleteSecretRequest request) { if (connection == null) { throw new ArgumentNullException(nameof(connection)); } -- 2.52.0 From 211fbcf34dbb662150de3c9d2bf2c8b22b6b9d3f Mon Sep 17 00:00:00 2001 From: GraceSolutions Date: Wed, 3 Jun 2026 20:06:13 -0400 Subject: [PATCH 11/12] Bulk v4 batch routes + strongly-typed -Secrets IDictionary[string,string][] - Endpoint registry: register POST/PATCH/DELETE /api/v4/secrets/batch as preferred candidates for BulkCreate/Update/Delete; v3 raw routes retained as automatic fallback. - DTOs: add projectId (required for v4) alongside workspaceId on the three batch request envelopes; both serialized when set, both ignored when null. - SecretsClient: populate ProjectId in CreateBatch/UpdateBatch/DeleteBatch so v4 succeeds on first attempt. - Cmdlets: -Secrets on New/Update-InfisicalSecret changed from Hashtable[] to IDictionary[] for stronger typing and tab-completion; converter rewritten to accept IEnumerable>. TagIds parsed from comma-separated string; nested Metadata dropped from bulk hashtable surface (still settable programmatically on bulk items). - Tests: 166 passing (was 161). Bulk endpoints now resolve to v4 primary with v3 fallback; new tests verify projectId envelope serialization, dual-key omission, and TagIds trimming. --- CHANGELOG.md | 11 +- Module/PSInfisicalAPI/PSInfisicalAPI.psd1 | 4 +- Module/PSInfisicalAPI/bin/PSInfisicalAPI.dll | Bin 177152 -> 177152 bytes .../BulkSecretConverterTests.cs | 35 +++--- .../BulkSecretDtoTests.cs | 29 +++++ .../EndpointRegistryTests.cs | 20 ++- .../Cmdlets/NewInfisicalSecretCmdlet.cs | 4 +- .../Cmdlets/UpdateInfisicalSecretCmdlet.cs | 4 +- .../Endpoints/InfisicalEndpointRegistry.cs | 36 ++++++ .../Secrets/InfisicalBulkSecretConverter.cs | 117 ++++++------------ .../Secrets/InfisicalSecretDtos.cs | 9 +- .../Secrets/InfisicalSecretsClient.cs | 3 + 12 files changed, 158 insertions(+), 114 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 251ad61..c63b5cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,19 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) loos ## Unreleased +## 2026.06.04.0005 + +- Build produced from commit e0a6ef02df3e. + +## Unreleased (carried forward) + +- **Bulk v4 batch routes**: Endpoint registry now registers `POST|PATCH|DELETE /api/v4/secrets/batch` as the preferred candidates for `BulkCreateSecret`/`BulkUpdateSecret`/`BulkDeleteSecret`; the existing v3 raw routes (`/api/v3/secrets/batch/raw`) remain as automatic fallback. Batch request DTOs serialize both `projectId` (required by v4) and `workspaceId` (accepted by v3) when populated. +- **Strongly-typed bulk input**: `-Secrets` on `New-InfisicalSecret` and `Update-InfisicalSecret` is now `IDictionary[]` instead of `Hashtable[]`. `InfisicalBulkSecretConverter` accepts `IEnumerable>` and parses `TagIds` from a comma-separated string. Nested `Metadata`/`SecretMetadata` dictionaries are no longer accepted in the bulk hashtable surface (set `SecretMetadata` programmatically on `InfisicalBulkCreateSecretItem`/`InfisicalBulkUpdateSecretItem` if needed). + ## 2026.06.03.2207 - Build produced from commit 09c3d5c68bbc. -- **M9 — Bulk, Duplicate & Inheritance**: +- **M9 — Bulk, Duplicate & Inheritance**: - **Bulk parameter sets** added to `New-InfisicalSecret`, `Update-InfisicalSecret`, and `Remove-InfisicalSecret` accepting `-Secrets Hashtable[]`; client methods `CreateBatch`/`UpdateBatch`/`DeleteBatch` wrap `POST|PATCH|DELETE /api/v3/secrets/batch/raw`. - **`Copy-InfisicalSecret`** cmdlet added, wrapping `POST /api/v4/secrets/duplicate` with source/destination environment + path parameters and per-attribute copy toggles. - **Connection inheritance** centralized in `InfisicalCmdletBase` (`ResolveProjectId`/`ResolveEnvironment`/`ResolveSecretPath`/`ResolveApiVersion`/`ResolveOrganizationId`). Explicit parameters always win; missing values fall back to the active connection and emit a `-Verbose` line. diff --git a/Module/PSInfisicalAPI/PSInfisicalAPI.psd1 b/Module/PSInfisicalAPI/PSInfisicalAPI.psd1 index bd0557e..2d0e294 100644 --- a/Module/PSInfisicalAPI/PSInfisicalAPI.psd1 +++ b/Module/PSInfisicalAPI/PSInfisicalAPI.psd1 @@ -1,6 +1,6 @@ @{ RootModule = 'PSInfisicalAPI.psm1' - ModuleVersion = '2026.06.03.2207' + ModuleVersion = '2026.06.04.0005' GUID = 'b8a2f3d4-7c51-4d2f-9e6a-1f0c8b3d4e51' Author = 'Grace Solutions' CompanyName = 'Grace Solutions' @@ -51,7 +51,7 @@ LicenseUri = 'https://www.gnu.org/licenses/agpl-3.0.html' ProjectUri = 'https://prod.git.gracesolution.info/gsadmin/PSInfisicalAPI' ReleaseNotes = 'See CHANGELOG.md in the project repository for release history.' - CommitHash = '09c3d5c68bbc' + CommitHash = 'e0a6ef02df3e' } } } \ No newline at end of file diff --git a/Module/PSInfisicalAPI/bin/PSInfisicalAPI.dll b/Module/PSInfisicalAPI/bin/PSInfisicalAPI.dll index ca605fc20afc9c2116736db3c473c1507b503b87..c2228bd2b65eb15cce929867338949db1dc619ef 100644 GIT binary patch literal 177152 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 z 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 q#dzSAGj=$-k|ax2&Qk=v!= z(Xwu}LNaWrT)FYDE+S;P?H`Mc^ za}dt!y|!sB8R=!@!;t^55tR?Ow`BcEk&KT8=^vZXlFN^L(kRR*^^>!N8Z(6>pJ~YZ zOlu3PPV#03D)eDP*2q0UqYwM?FTc<|a*w5(0=9<8=W9rRKmYqh-;b_gjfqUYMg%@6 z0U78c)L)j#X&-qLiWbPwWjDDn%e{rgsWMofv4#)V!H4-RQ}QEk%a7a*ALcW{_vA-D z0RI{KN5Cet=wv2FZWqaIAHAhj!Pb{^Et*q{dKtOfD4Ew-&3uRXv1udsvL@%}+N9_d z=%MkJjtnWSt#UQ`rW1tK=)IWAYGZFC^DdN%tWZPI$4P1*y&L5;Us`JuB?DWa^X!bUXx6L+6E7>~|Ir@=+COT`)pyT1Gh!Pt%3J*~dB3e&m zyR~BLkH84*nTXPZB1)rYPBEJ5K^EJC`4;pkGqK7D$>_46%qmlI*&JDPfif6f+fZoj z3D6p3JE|#^s|T&PFY;R*dC(UY`IYss$ja5|qT>+a8%AK8EZ5fj4KKiUfF&fDoEvTO z$Rqh1Zfel+NCwm$ir8J<@1aPUx`-8 zk6vx2{8z|Cz7uH{Rn*!#r6t=w@@*`Fk%y*qWW9VROX1t|Oz)!)$;Lfeo+Or6zVih1 zQMArSZ_SV12C)yA>A2Omt{z}S4=|#z;H&M#+#D2qwJCnHTjX^OMP3(9(gSvqZuBdU zJm61~{K_QV=ugs(NFY2m`hE1y$)ZG1#-n#G5MCP$ar;47z$yTv17%yvk34F&rRfF> zTCp3}5Bwary01V`nJt-l=<<8m)4h4eX)$%?A@lRJVcR{1+dmdFh^5ehRVu5ZwH3k5 z->@xz!>;y`&*yLGl?Gt>AM%}M(tcp9K@k`<0i9FPn>VFp5{;lj*hj^3Z6E#daa^_7 zfws|q8*1KIbCi|69!VW}tuCm3-fjli%b^7ozkatCfi%kf4)4W*tcOJ*;0vC}JAg(ZgqR)2Jc zwI9xR%3d^j4|xw$nd_ORa(;xB4c$LAy`h!)#n`80{yNZTAkG%o-aZPkbIokEVJkv^eT(&L)BnX^;9o zCH;)XCFZHg&_NJXi5`fW7e$nknRDV{!>HDYjWI7Lt1)yJWz9>Te@2-nryVgy*(Yq{ zJWOIE@p+jiM^NtL8gBL+_nwznhI^14%h*VB!83YpMn`aSPqQY&0?nEZ3pDHECw@F^}~O^Ub#HYwr~ znqDLn9e0t{R6=yz?3n1pGF*~k87?7i#&H3^tdgmz6B_wrGx9D!{3N$CKgsRJCY{)P zoll8YcUycdAJ@2oAv6BKKt8!zk55~pXU6Z-O`fp3_n|!{*{GGS=oW~lrghR-=xOOw zEu3|asIwkw_~iJ=8=FF&;{#6=Xk=Amfu}d0fX1Sp=c#PJ*$ddbGn4X*ve-@-P-e@W zMuPm6CYeZLtDdC&oVEV%x&H4e|92y$SJ~1!GiiRCpL~h`d%6F6CB=pWm;Ev~2^-^9 z18ZxnD_Q255_mSW#i$V_T4>PhpY2n1qG z1?19F`zRFVN2gDoG#_6UlRqv<7mTV0h*vQHkGjI zWHm)WFcZenrcX;{pD%KWw&%7$M3gf=qg2e||$@N~oj z7W>m8I3D#M^vem^?s2gLSsNm%@nAnEcj z>GB}y$}s6lX>`amI@AIKM;^(qnr3+Ph++DwNs2}C)3zx->ql;4z{0}E?Xc{{W9GsR zL)NKnUa(2Tir}|@!g8yPxF=*WnJzrew7oEDWId9{1-IxCaZ zeWROvXPZnV>_6A)kFeCu^E+_Jh--?kY4fCh1Q&($k@wqk z1A~T%_e6;{CUB4S1@CEm6I^#Po)Wd zL?nidNDP|@r%{upKPfwMlbyuJ{YiY>UOwhuJ~qv)Nq^-T`IL1x1!u0kh<3$Q!+$pj zE{12RzKe2(lXJ}V=(PE04S#yVrO5iU5lRXFG05Mxq8<2sn_q$;scB^71M?N}$J^z9 zGlBL5D5_5M(X4j=_(cL8eiPF=vP{>5c5aLs^Km zP0oEb=RT8jFEeZfnN>8IE=RA#z3enh5nubtDWX#!BBn8VWs9uoZxcz8I3N>3h>jnL zpoq48K!$JBxxuW2TGU8rYrCj)Kf$I51*10_+D30Q7q{7q+d}r-7_sL@h%yb_*w%*f z1hovgu`SOOnnC!hkNKOS#FIyr_(?27S#9hP9C?A6Vf$-zvSKcTwReiTlEp(w!>Z3) zR(&4P_760RIR(vx+2kPe9ykJXF+$|`$McDPkv#Ha`*Xhug0a6KC=xPf|F+Hj!CZXN zUVL#Tf^R4#+GV3)oXt{t)D{^1qFI{va!o25zEtBUnzzyEk}>jIlRZsh^fD<3yNQXl zbl7kRVXnYXm`V!)-p2lWLoa)`-I-@Aw(DN%2MLCxMWVckdsr@=K0_ibK92tSZ9D5` zI7lF_H}YF(BtryJE)hsBMXsgo%p%p+y2wiZwCo}_?-s6P(<4U*Ico#t+yGe>ARDF9 zD!-EEf)JD6-X#HYd4OCgg@??Q*;go?AGvjs`MGVfUv1?5A$CuW@+0?11*_o}iH4I# zhNXY7eL5Pfd}JtC#9q3$=sOipvUT756V&(Z7FXksy$#W^Abz`idPr zm9qIfJr7Dw^Hj_3V!2qTQKH>b(^f27an_QvmaST{ddV`S)_ZELNm+K*64I6}TekWP zx7F2stJW-ExoX{t)hk!8Te+fe)>&)TtXREjZQohTxesQhr`qCQwN+}ar;d-OE$QV` z=8$q8r0`Ya!D3&1a`^pT8T{;x#r=ihvU~-cQP+!Az5y-cpT1|XbQnez%JOmUHNtD9 zmNYc(w+J(9OTt#8M5PvaYBJX}RPU|K4U5>X4M*SlRciD?+r#J<`~BP{U6pbfVolRq zii5>!sc%VC;mQ@4UFH|GA4NX1W3X7S1vA_cOfl0Y|DD;<#Sr-@@cO~xaJ^b6pV_f% zcwf2H$48udD~F1M>-+lF>|4Ha|Ng%I75)AFtCwGKX4t-bO_)ywFL6IB{1@-;iobM= zu=ISNrE8Blme#AK!2^Hk5nu6F;Nc3K`>x8+(HRY*D~e}U&+yPtrCP7`9IOnN`}qKL zU$Iv6mRPQz*05BzSNe-(XO-9`V3zC@)J|XH+Ei-fR@>6Zi~RZHTjGTS#ew2reFVfLGdspAc4oLFR!($DtX#ft|H`$i&nonT9JsgUklQs-TGU9aE464kstxXGC z>n$AMl~I=gk#DjDb{7XKhl{C98jG4eBQF_ig|g!f z?k`mbx(9{|{{yRedaOG+)*AUrIJU-_{9iq+uZF`)h}-|_as4_RS2SIAO=QMc6UVv~ zHea-pdN3Nw#gTst#~oX>|C{G>9YfkP)L*ElYn3q+(yFj#Bm{Q<8l%$cuu@`0j8W^7 zuvXO7UqlD9efXn~P3Rb;wBXy7`jx`R%j^|oGIm<6Jj^aA*dYFkQd2yY>#LM2SOjgj zAwzcW?xr+1h))%FuZE^AN`mC~Rg3VyzmwfvE{Dx@n9IDUriaDZKG`Uxm%72kt#dOi zQRJaeU+vSvNcJ3Nm#O0?Rrc4HJhO(~wqv+lzNk@)d*jnoEFVesaq762ASD}88pd3PIuO$XyY-)))lxn*x zb&=a1(}k^9l_baz(UvfQt+%~cFQ8%w9mujdB*e%PIV>`0V->a>;w?id4H)N!*VM5* zZ4QF-`WRWq+#H!DSBLM%9%XN8oWNs`#J72BR_#z}Xgk4@5-O>eAA1CfC=M2n^ca;P!M~*xyN>I-#Fqh|-|3 zN@95e43ip}o9vAkYFSb9!!fv@ zix?E@oD1w5t`}>)6$!c@b7CNVBT~>;Et!Kxd!|>Z9w-c!t}&u*EoxH4@}BLfe1SN9 zS7E4BFOuui{_phw`;9tnb%>@5F-S{aRBifER-i^qP z?(*tb+%RQFcP9AQW1>uUQl^eMIwd?ZWZYIG%7rTasw@fdHxYe7#l_rj4r~x{aWU-$ z0xf2NR6gcLH6r#@f2qm|P^Eh8X(z+#O06WGKWr3BVaUHc=2&@}oK^L<9XIw}2OKG; zmdcqk`!nSRk6t~QV^P8yG9xJswmkDlU2b4lJdwh!J4^k2QhLcmN>A>*hC2$RIH!T* zQ!-^s-%xj9K+1PC8CUDJ{=(42sSQd$E!n+pH?HUAYGq(oX{ac^ymtxfs)1jvt-C^j zQRJo6>eZ(tnmYgJu4-k7dpgXWG9Jf~a;J^%?w@Q8BV&26%`tiI^mGw+ilJT*Erd662~kc z?XpniCu=wG^+~S8+e?w^==|~-PonlTW(mo)H_nXNy^#6jyKhoE*8TQzg)*rmvgZ$% z52Xt070K3~@Z_hA*FSlhYsD)k^H!T;MV*)~``49!oqnTf?)&R>5SxkxX2B-e9Z~Vr zCM9Cch>=|AlUreombL-#;?+a!o3(B>CUH>ea?e$-36CRT>Py?!JfOVoyJYb`pTBeO z)9T!J*t_>6Uj%6;e+&8h1bLru@<>w032^@H?%CM0e%gIEUG#4q-M5^(@QYI}%)MON z=sNrIJvAcqmv@$j4i<{TwI$mt_2T8bi)Np!R7=I$ODytv105l6;GLj z?Oa4V@{O!$oGYMqg*uBjW}XGMir>}z$q0monbZ}o*>AbXd+N&0D8}l03C}&sx4YyY z_nOnrHu{#22$c<2Ri81phl zaTdS(4X6A0UCGsIW^5s_v?b@(G&E80Nv)<)_S~~4TH*hd`~MRM{y)o%dlCQu literal 177152 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 (1Nx7WEiHgJ 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

Xp`fH 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 entry = new Dictionary { { "SecretName", "API_KEY" }, { "SecretValue", "abc" }, { "SecretComment", "primary" }, - { "SkipMultilineEncoding", true }, - { "TagIds", new[] { "tag-1", "tag-2" } } + { "SkipMultilineEncoding", "true" }, + { "TagIds", "tag-1,tag-2" } }; - InfisicalBulkCreateSecretItem[] items = InfisicalBulkSecretConverter.ToCreateItems(new[] { entry }); + InfisicalBulkCreateSecretItem[] items = InfisicalBulkSecretConverter.ToCreateItems(new IDictionary[] { entry }); Assert.Single(items); Assert.Equal("API_KEY", items[0].SecretName); Assert.Equal("abc", items[0].SecretValue); @@ -31,13 +31,13 @@ namespace PSInfisicalAPI.Tests [Fact] public void ToCreateItems_Accepts_Name_Alias_For_SecretName() { - Hashtable entry = new Hashtable + Dictionary entry = new Dictionary { { "Name", "API_KEY" }, { "Value", "abc" } }; - InfisicalBulkCreateSecretItem[] items = InfisicalBulkSecretConverter.ToCreateItems(new[] { entry }); + InfisicalBulkCreateSecretItem[] items = InfisicalBulkSecretConverter.ToCreateItems(new IDictionary[] { entry }); Assert.Single(items); Assert.Equal("API_KEY", items[0].SecretName); Assert.Equal("abc", items[0].SecretValue); @@ -46,23 +46,23 @@ namespace PSInfisicalAPI.Tests [Fact] public void ToCreateItems_Without_SecretName_Throws() { - Hashtable entry = new Hashtable { { "Value", "abc" } }; + Dictionary entry = new Dictionary { { "Value", "abc" } }; Assert.Throws(() => - InfisicalBulkSecretConverter.ToCreateItems(new[] { entry })); + InfisicalBulkSecretConverter.ToCreateItems(new IDictionary[] { entry })); } [Fact] public void ToUpdateItems_Maps_NewSecretName() { - Hashtable entry = new Hashtable + Dictionary entry = new Dictionary { { "SecretName", "API_KEY" }, { "NewSecretName", "RENAMED" }, { "SecretValue", "new-value" } }; - InfisicalBulkUpdateSecretItem[] items = InfisicalBulkSecretConverter.ToUpdateItems(new[] { entry }); + InfisicalBulkUpdateSecretItem[] items = InfisicalBulkSecretConverter.ToUpdateItems(new IDictionary[] { entry }); Assert.Single(items); Assert.Equal("API_KEY", items[0].SecretName); Assert.Equal("RENAMED", items[0].NewSecretName); @@ -70,20 +70,17 @@ namespace PSInfisicalAPI.Tests } [Fact] - public void ToCreateItems_Maps_Metadata_Dictionary() + public void ToCreateItems_Trims_TagId_Whitespace_And_Skips_Empty() { - Hashtable meta = new Hashtable { { "owner", "platform" }, { "tier", "1" } }; - Hashtable entry = new Hashtable + Dictionary entry = new Dictionary { { "SecretName", "API_KEY" }, { "SecretValue", "abc" }, - { "Metadata", meta } + { "TagIds", " tag-1 , , tag-2 " } }; - InfisicalBulkCreateSecretItem[] items = InfisicalBulkSecretConverter.ToCreateItems(new[] { entry }); - Assert.NotNull(items[0].SecretMetadata); - Assert.Equal("platform", items[0].SecretMetadata["owner"]); - Assert.Equal("1", items[0].SecretMetadata["tier"]); + InfisicalBulkCreateSecretItem[] items = InfisicalBulkSecretConverter.ToCreateItems(new IDictionary[] { entry }); + Assert.Equal(new[] { "tag-1", "tag-2" }, items[0].TagIds); } [Fact] diff --git a/src/PSInfisicalAPI.Tests/BulkSecretDtoTests.cs b/src/PSInfisicalAPI.Tests/BulkSecretDtoTests.cs index b4a4474..60af355 100644 --- a/src/PSInfisicalAPI.Tests/BulkSecretDtoTests.cs +++ b/src/PSInfisicalAPI.Tests/BulkSecretDtoTests.cs @@ -59,12 +59,14 @@ namespace PSInfisicalAPI.Tests object dto = MakeDto("PSInfisicalAPI.Secrets.InfisicalSecretBatchCreateRequestDto"); dto.GetType().GetProperty("WorkspaceId").SetValue(dto, "wks-1"); + dto.GetType().GetProperty("ProjectId").SetValue(dto, "wks-1"); dto.GetType().GetProperty("Environment").SetValue(dto, "prod"); dto.GetType().GetProperty("SecretPath").SetValue(dto, "/db"); dto.GetType().GetProperty("Secrets").SetValue(dto, list); JObject json = JObject.Parse(JsonConvert.SerializeObject(dto)); Assert.Equal("wks-1", (string)json["workspaceId"]); + Assert.Equal("wks-1", (string)json["projectId"]); Assert.Equal("prod", (string)json["environment"]); Assert.Equal("/db", (string)json["secretPath"]); JArray secrets = (JArray)json["secrets"]; @@ -73,6 +75,31 @@ namespace PSInfisicalAPI.Tests Assert.Equal("V1", (string)secrets[0]["secretValue"]); } + [Fact] + public void BatchCreateRequest_Omits_Null_WorkspaceId_When_Only_ProjectId_Set() + { + object dto = MakeDto("PSInfisicalAPI.Secrets.InfisicalSecretBatchCreateRequestDto"); + dto.GetType().GetProperty("ProjectId").SetValue(dto, "wks-1"); + dto.GetType().GetProperty("Environment").SetValue(dto, "prod"); + + JObject json = JObject.Parse(JsonConvert.SerializeObject(dto)); + Assert.False(json.ContainsKey("workspaceId")); + Assert.Equal("wks-1", (string)json["projectId"]); + } + + [Fact] + public void BatchUpdateRequest_Includes_ProjectId_Alongside_WorkspaceId() + { + object dto = MakeDto("PSInfisicalAPI.Secrets.InfisicalSecretBatchUpdateRequestDto"); + dto.GetType().GetProperty("WorkspaceId").SetValue(dto, "wks-1"); + dto.GetType().GetProperty("ProjectId").SetValue(dto, "wks-1"); + dto.GetType().GetProperty("Environment").SetValue(dto, "prod"); + + JObject json = JObject.Parse(JsonConvert.SerializeObject(dto)); + Assert.Equal("wks-1", (string)json["workspaceId"]); + Assert.Equal("wks-1", (string)json["projectId"]); + } + [Fact] public void BatchDeleteRequest_Serializes_With_Secret_Keys() { @@ -86,11 +113,13 @@ namespace PSInfisicalAPI.Tests object dto = MakeDto("PSInfisicalAPI.Secrets.InfisicalSecretBatchDeleteRequestDto"); dto.GetType().GetProperty("WorkspaceId").SetValue(dto, "wks-1"); + dto.GetType().GetProperty("ProjectId").SetValue(dto, "wks-1"); dto.GetType().GetProperty("Environment").SetValue(dto, "prod"); dto.GetType().GetProperty("Secrets").SetValue(dto, list); JObject json = JObject.Parse(JsonConvert.SerializeObject(dto)); Assert.Equal("wks-1", (string)json["workspaceId"]); + Assert.Equal("wks-1", (string)json["projectId"]); JArray secrets = (JArray)json["secrets"]; Assert.Single(secrets); Assert.Equal("K1", (string)secrets[0]["secretKey"]); diff --git a/src/PSInfisicalAPI.Tests/EndpointRegistryTests.cs b/src/PSInfisicalAPI.Tests/EndpointRegistryTests.cs index d931695..190a3ad 100644 --- a/src/PSInfisicalAPI.Tests/EndpointRegistryTests.cs +++ b/src/PSInfisicalAPI.Tests/EndpointRegistryTests.cs @@ -71,9 +71,9 @@ namespace PSInfisicalAPI.Tests [InlineData(InfisicalEndpointNames.LdapAuthLogin, "POST", "/api/v1/auth/ldap-auth/login")] [InlineData(InfisicalEndpointNames.AzureAuthLogin, "POST", "/api/v1/auth/azure-auth/login")] [InlineData(InfisicalEndpointNames.GcpIamAuthLogin, "POST", "/api/v1/auth/gcp-auth/login")] - [InlineData(InfisicalEndpointNames.BulkCreateSecret, "POST", "/api/v3/secrets/batch/raw")] - [InlineData(InfisicalEndpointNames.BulkUpdateSecret, "PATCH", "/api/v3/secrets/batch/raw")] - [InlineData(InfisicalEndpointNames.BulkDeleteSecret, "DELETE", "/api/v3/secrets/batch/raw")] + [InlineData(InfisicalEndpointNames.BulkCreateSecret, "POST", "/api/v4/secrets/batch")] + [InlineData(InfisicalEndpointNames.BulkUpdateSecret, "PATCH", "/api/v4/secrets/batch")] + [InlineData(InfisicalEndpointNames.BulkDeleteSecret, "DELETE", "/api/v4/secrets/batch")] [InlineData(InfisicalEndpointNames.DuplicateSecret, "POST", "/api/v4/secrets/duplicate")] public void Registered_Endpoints_Have_Expected_Shape(string name, string method, string template) { @@ -81,5 +81,19 @@ namespace PSInfisicalAPI.Tests Assert.Equal(method, definition.Method); Assert.Equal(template, definition.Template); } + + [Theory] + [InlineData(InfisicalEndpointNames.BulkCreateSecret, "POST", "/api/v3/secrets/batch/raw")] + [InlineData(InfisicalEndpointNames.BulkUpdateSecret, "PATCH", "/api/v3/secrets/batch/raw")] + [InlineData(InfisicalEndpointNames.BulkDeleteSecret, "DELETE", "/api/v3/secrets/batch/raw")] + public void Bulk_Endpoints_Retain_V3_Fallback_Candidate(string name, string method, string template) + { + System.Collections.Generic.IReadOnlyList candidates = InfisicalEndpointRegistry.GetCandidates(name); + Assert.Equal(2, candidates.Count); + Assert.Equal("v4", candidates[0].Version); + Assert.Equal("v3", candidates[1].Version); + Assert.Equal(method, candidates[1].Method); + Assert.Equal(template, candidates[1].Template); + } } } diff --git a/src/PSInfisicalAPI/Cmdlets/NewInfisicalSecretCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/NewInfisicalSecretCmdlet.cs index 2935f2e..f853073 100644 --- a/src/PSInfisicalAPI/Cmdlets/NewInfisicalSecretCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/NewInfisicalSecretCmdlet.cs @@ -1,5 +1,5 @@ using System; -using System.Collections; +using System.Collections.Generic; using System.Management.Automation; using System.Security; using PSInfisicalAPI.Connections; @@ -24,7 +24,7 @@ namespace PSInfisicalAPI.Cmdlets public SecureString SecureSecretValue { get; set; } [Parameter(Mandatory = true, Position = 0, ParameterSetName = "Bulk", ValueFromPipeline = true)] - public Hashtable[] Secrets { get; set; } + public IDictionary[] Secrets { get; set; } [Parameter] public string SecretComment { get; set; } [Parameter] public string ProjectId { get; set; } diff --git a/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalSecretCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalSecretCmdlet.cs index 0bb6912..9ebd1f6 100644 --- a/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalSecretCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalSecretCmdlet.cs @@ -1,5 +1,5 @@ using System; -using System.Collections; +using System.Collections.Generic; using System.Management.Automation; using System.Security; using PSInfisicalAPI.Connections; @@ -21,7 +21,7 @@ namespace PSInfisicalAPI.Cmdlets [Parameter(ParameterSetName = "SecureString")] public SecureString SecureSecretValue { get; set; } [Parameter(Mandatory = true, Position = 0, ParameterSetName = "Bulk", ValueFromPipeline = true)] - public Hashtable[] Secrets { get; set; } + public IDictionary[] Secrets { get; set; } [Parameter] public string NewSecretName { get; set; } [Parameter] public string SecretComment { get; set; } diff --git a/src/PSInfisicalAPI/Endpoints/InfisicalEndpointRegistry.cs b/src/PSInfisicalAPI/Endpoints/InfisicalEndpointRegistry.cs index 332de5a..4aceb25 100644 --- a/src/PSInfisicalAPI/Endpoints/InfisicalEndpointRegistry.cs +++ b/src/PSInfisicalAPI/Endpoints/InfisicalEndpointRegistry.cs @@ -198,6 +198,18 @@ namespace PSInfisicalAPI.Endpoints ContainsSecretMaterialInResponse = true }); + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.BulkCreateSecret, + Resource = "Secrets", + Version = "v4", + Method = "POST", + Template = "/api/v4/secrets/batch", + RequiresAuthorization = true, + ContainsSecretMaterialInRequest = true, + ContainsSecretMaterialInResponse = true + }); + Add(map, new InfisicalEndpointDefinition { Name = InfisicalEndpointNames.BulkCreateSecret, @@ -210,6 +222,18 @@ namespace PSInfisicalAPI.Endpoints ContainsSecretMaterialInResponse = true }); + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.BulkUpdateSecret, + Resource = "Secrets", + Version = "v4", + Method = "PATCH", + Template = "/api/v4/secrets/batch", + RequiresAuthorization = true, + ContainsSecretMaterialInRequest = true, + ContainsSecretMaterialInResponse = true + }); + Add(map, new InfisicalEndpointDefinition { Name = InfisicalEndpointNames.BulkUpdateSecret, @@ -222,6 +246,18 @@ namespace PSInfisicalAPI.Endpoints ContainsSecretMaterialInResponse = true }); + Add(map, new InfisicalEndpointDefinition + { + Name = InfisicalEndpointNames.BulkDeleteSecret, + Resource = "Secrets", + Version = "v4", + Method = "DELETE", + Template = "/api/v4/secrets/batch", + RequiresAuthorization = true, + ContainsSecretMaterialInRequest = true, + ContainsSecretMaterialInResponse = true + }); + Add(map, new InfisicalEndpointDefinition { Name = InfisicalEndpointNames.BulkDeleteSecret, diff --git a/src/PSInfisicalAPI/Secrets/InfisicalBulkSecretConverter.cs b/src/PSInfisicalAPI/Secrets/InfisicalBulkSecretConverter.cs index ba4f37d..8b7aab5 100644 --- a/src/PSInfisicalAPI/Secrets/InfisicalBulkSecretConverter.cs +++ b/src/PSInfisicalAPI/Secrets/InfisicalBulkSecretConverter.cs @@ -1,29 +1,27 @@ using System; -using System.Collections; using System.Collections.Generic; using PSInfisicalAPI.Errors; -using PSInfisicalAPI.Security; namespace PSInfisicalAPI.Secrets { public static class InfisicalBulkSecretConverter { - public static InfisicalBulkCreateSecretItem[] ToCreateItems(IEnumerable input) + public static InfisicalBulkCreateSecretItem[] ToCreateItems(IEnumerable> input) { if (input == null) { return new InfisicalBulkCreateSecretItem[0]; } List list = new List(); - foreach (object element in input) + foreach (IDictionary entry in input) { - Hashtable table = AsHashtable(element); + if (entry == null) { continue; } + IDictionary table = Normalize(entry); InfisicalBulkCreateSecretItem item = new InfisicalBulkCreateSecretItem { SecretName = GetString(table, "SecretName", "Name", "Key", "SecretKey"), - SecretValue = GetSecretValue(table, "SecretValue", "Value"), + SecretValue = GetString(table, "SecretValue", "Value"), SecretComment = GetString(table, "SecretComment", "Comment"), SkipMultilineEncoding = GetBool(table, "SkipMultilineEncoding"), - TagIds = GetStringArray(table, "TagIds"), - SecretMetadata = GetStringDictionary(table, "SecretMetadata", "Metadata") + TagIds = GetStringArray(table, "TagIds") }; if (string.IsNullOrEmpty(item.SecretName)) @@ -37,23 +35,23 @@ namespace PSInfisicalAPI.Secrets return list.ToArray(); } - public static InfisicalBulkUpdateSecretItem[] ToUpdateItems(IEnumerable input) + public static InfisicalBulkUpdateSecretItem[] ToUpdateItems(IEnumerable> input) { if (input == null) { return new InfisicalBulkUpdateSecretItem[0]; } List list = new List(); - foreach (object element in input) + foreach (IDictionary entry in input) { - Hashtable table = AsHashtable(element); + if (entry == null) { continue; } + IDictionary table = Normalize(entry); InfisicalBulkUpdateSecretItem item = new InfisicalBulkUpdateSecretItem { SecretName = GetString(table, "SecretName", "Name", "Key", "SecretKey"), NewSecretName = GetString(table, "NewSecretName", "NewName"), - SecretValue = GetSecretValue(table, "SecretValue", "Value"), + SecretValue = GetString(table, "SecretValue", "Value"), SecretComment = GetString(table, "SecretComment", "Comment"), SkipMultilineEncoding = GetBool(table, "SkipMultilineEncoding"), - TagIds = GetStringArray(table, "TagIds"), - SecretMetadata = GetStringDictionary(table, "SecretMetadata", "Metadata") + TagIds = GetStringArray(table, "TagIds") }; if (string.IsNullOrEmpty(item.SecretName)) @@ -67,98 +65,53 @@ namespace PSInfisicalAPI.Secrets return list.ToArray(); } - private static Hashtable AsHashtable(object element) + private static IDictionary Normalize(IDictionary source) { - if (element is Hashtable hashtable) { return hashtable; } - if (element is IDictionary dictionary) + Dictionary normalized = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (KeyValuePair kvp in source) { - Hashtable converted = new Hashtable(StringComparer.OrdinalIgnoreCase); - foreach (DictionaryEntry entry in dictionary) - { - if (entry.Key == null) { continue; } - converted[entry.Key.ToString()] = entry.Value; - } - - return converted; + if (string.IsNullOrEmpty(kvp.Key)) { continue; } + normalized[kvp.Key] = kvp.Value; } - throw new InfisicalConfigurationException("Bulk secret entries must be Hashtable or IDictionary values."); + return normalized; } - private static string GetString(Hashtable table, params string[] keys) + private static string GetString(IDictionary table, params string[] keys) { foreach (string key in keys) { - if (table.ContainsKey(key) && table[key] != null) + string value; + if (table.TryGetValue(key, out value) && !string.IsNullOrEmpty(value)) { - return table[key].ToString(); + return value; } } return null; } - private static string GetSecretValue(Hashtable table, params string[] keys) + private static bool? GetBool(IDictionary table, string key) { - foreach (string key in keys) - { - if (!table.ContainsKey(key)) { continue; } - object value = table[key]; - if (value == null) { return null; } - if (value is System.Security.SecureString secure) - { - return SecureStringUtility.UsePlainText(secure, plain => plain); - } - - return value.ToString(); - } - - return null; - } - - private static bool? GetBool(Hashtable table, string key) - { - if (!table.ContainsKey(key) || table[key] == null) { return null; } - object value = table[key]; - if (value is bool b) { return b; } + string value; + if (!table.TryGetValue(key, out value) || string.IsNullOrEmpty(value)) { return null; } bool parsed; - return bool.TryParse(value.ToString(), out parsed) ? parsed : (bool?)null; + return bool.TryParse(value, out parsed) ? parsed : (bool?)null; } - private static string[] GetStringArray(Hashtable table, string key) + private static string[] GetStringArray(IDictionary table, string key) { - if (!table.ContainsKey(key) || table[key] == null) { return null; } - object value = table[key]; - if (value is string[] direct) { return direct; } - if (value is IEnumerable enumerable && !(value is string)) + string value; + if (!table.TryGetValue(key, out value) || string.IsNullOrEmpty(value)) { return null; } + string[] parts = value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + List trimmed = new List(parts.Length); + foreach (string part in parts) { - List items = new List(); - foreach (object item in enumerable) { if (item != null) { items.Add(item.ToString()); } } - return items.ToArray(); + string item = part.Trim(); + if (!string.IsNullOrEmpty(item)) { trimmed.Add(item); } } - return new[] { value.ToString() }; - } - - private static Dictionary GetStringDictionary(Hashtable table, params string[] keys) - { - foreach (string key in keys) - { - if (!table.ContainsKey(key) || table[key] == null) { continue; } - if (table[key] is IDictionary dictionary) - { - Dictionary result = new Dictionary(StringComparer.OrdinalIgnoreCase); - foreach (DictionaryEntry entry in dictionary) - { - if (entry.Key == null) { continue; } - result[entry.Key.ToString()] = entry.Value != null ? entry.Value.ToString() : null; - } - - return result; - } - } - - return null; + return trimmed.Count == 0 ? null : trimmed.ToArray(); } } } diff --git a/src/PSInfisicalAPI/Secrets/InfisicalSecretDtos.cs b/src/PSInfisicalAPI/Secrets/InfisicalSecretDtos.cs index d4dd9cc..7f4caab 100644 --- a/src/PSInfisicalAPI/Secrets/InfisicalSecretDtos.cs +++ b/src/PSInfisicalAPI/Secrets/InfisicalSecretDtos.cs @@ -117,7 +117,8 @@ namespace PSInfisicalAPI.Secrets internal sealed class InfisicalSecretBatchCreateRequestDto { - [JsonProperty("workspaceId")] public string WorkspaceId { get; set; } + [JsonProperty("workspaceId", NullValueHandling = NullValueHandling.Ignore)] public string WorkspaceId { get; set; } + [JsonProperty("projectId", NullValueHandling = NullValueHandling.Ignore)] public string ProjectId { get; set; } [JsonProperty("environment")] public string Environment { get; set; } [JsonProperty("secretPath", NullValueHandling = NullValueHandling.Ignore)] public string SecretPath { get; set; } [JsonProperty("secrets")] public List Secrets { get; set; } @@ -125,7 +126,8 @@ namespace PSInfisicalAPI.Secrets internal sealed class InfisicalSecretBatchUpdateRequestDto { - [JsonProperty("workspaceId")] public string WorkspaceId { get; set; } + [JsonProperty("workspaceId", NullValueHandling = NullValueHandling.Ignore)] public string WorkspaceId { get; set; } + [JsonProperty("projectId", NullValueHandling = NullValueHandling.Ignore)] public string ProjectId { get; set; } [JsonProperty("environment")] public string Environment { get; set; } [JsonProperty("secretPath", NullValueHandling = NullValueHandling.Ignore)] public string SecretPath { get; set; } [JsonProperty("mode", NullValueHandling = NullValueHandling.Ignore)] public string Mode { get; set; } @@ -134,7 +136,8 @@ namespace PSInfisicalAPI.Secrets internal sealed class InfisicalSecretBatchDeleteRequestDto { - [JsonProperty("workspaceId")] public string WorkspaceId { get; set; } + [JsonProperty("workspaceId", NullValueHandling = NullValueHandling.Ignore)] public string WorkspaceId { get; set; } + [JsonProperty("projectId", NullValueHandling = NullValueHandling.Ignore)] public string ProjectId { get; set; } [JsonProperty("environment")] public string Environment { get; set; } [JsonProperty("secretPath", NullValueHandling = NullValueHandling.Ignore)] public string SecretPath { get; set; } [JsonProperty("secrets")] public List Secrets { get; set; } diff --git a/src/PSInfisicalAPI/Secrets/InfisicalSecretsClient.cs b/src/PSInfisicalAPI/Secrets/InfisicalSecretsClient.cs index 9d7e93a..8e4161b 100644 --- a/src/PSInfisicalAPI/Secrets/InfisicalSecretsClient.cs +++ b/src/PSInfisicalAPI/Secrets/InfisicalSecretsClient.cs @@ -254,6 +254,7 @@ namespace PSInfisicalAPI.Secrets InfisicalSecretBatchCreateRequestDto dtoRequest = new InfisicalSecretBatchCreateRequestDto { WorkspaceId = resolvedProjectId, + ProjectId = resolvedProjectId, Environment = resolvedEnvironment, SecretPath = FirstNonEmpty(request.SecretPath, connection.DefaultSecretPath, "/"), Secrets = items @@ -309,6 +310,7 @@ namespace PSInfisicalAPI.Secrets InfisicalSecretBatchUpdateRequestDto dtoRequest = new InfisicalSecretBatchUpdateRequestDto { WorkspaceId = resolvedProjectId, + ProjectId = resolvedProjectId, Environment = resolvedEnvironment, SecretPath = FirstNonEmpty(request.SecretPath, connection.DefaultSecretPath, "/"), Mode = request.Mode, @@ -355,6 +357,7 @@ namespace PSInfisicalAPI.Secrets InfisicalSecretBatchDeleteRequestDto dtoRequest = new InfisicalSecretBatchDeleteRequestDto { WorkspaceId = resolvedProjectId, + ProjectId = resolvedProjectId, Environment = resolvedEnvironment, SecretPath = FirstNonEmpty(request.SecretPath, connection.DefaultSecretPath, "/"), Secrets = items -- 2.52.0 From 2cbd5c2008f5df44d9f7674b7157ced73391ad84 Mon Sep 17 00:00:00 2001 From: GraceSolutions Date: Wed, 3 Jun 2026 20:21:00 -0400 Subject: [PATCH 12/12] Add BulkSecretsTransformationAttribute for -Secrets parameter normalization Normalizes Hashtable, OrderedDictionary, PSObject-wrapped, and typed generic dictionaries into IDictionary[] before parameter binding, enabling native PowerShell @{...} and [ordered]@{...} literals against the strongly-typed -Secrets parameter on New-/Update-InfisicalSecret. Adds 8 transformation tests; 174/174 passing. --- CHANGELOG.md | 8 +- Module/PSInfisicalAPI/PSInfisicalAPI.psd1 | 4 +- Module/PSInfisicalAPI/bin/PSInfisicalAPI.dll | Bin 177152 -> 178688 bytes .../BulkSecretConverterTests.cs | 116 ++++++++++++++++++ .../BulkSecretsTransformationAttribute.cs | 94 ++++++++++++++ .../Cmdlets/NewInfisicalSecretCmdlet.cs | 1 + .../Cmdlets/UpdateInfisicalSecretCmdlet.cs | 1 + 7 files changed, 221 insertions(+), 3 deletions(-) create mode 100644 src/PSInfisicalAPI/Cmdlets/BulkSecretsTransformationAttribute.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index c63b5cb..080548b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) loos ## Unreleased +## 2026.06.04.0020 + +- Build produced from commit 211fbcf34dbb. + +## Unreleased (carried forward) + ## 2026.06.04.0005 - Build produced from commit e0a6ef02df3e. @@ -18,7 +24,7 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) loos ## 2026.06.03.2207 - Build produced from commit 09c3d5c68bbc. -- **M9 — Bulk, Duplicate & Inheritance**: +- **M9 — Bulk, Duplicate & Inheritance**: - **Bulk parameter sets** added to `New-InfisicalSecret`, `Update-InfisicalSecret`, and `Remove-InfisicalSecret` accepting `-Secrets Hashtable[]`; client methods `CreateBatch`/`UpdateBatch`/`DeleteBatch` wrap `POST|PATCH|DELETE /api/v3/secrets/batch/raw`. - **`Copy-InfisicalSecret`** cmdlet added, wrapping `POST /api/v4/secrets/duplicate` with source/destination environment + path parameters and per-attribute copy toggles. - **Connection inheritance** centralized in `InfisicalCmdletBase` (`ResolveProjectId`/`ResolveEnvironment`/`ResolveSecretPath`/`ResolveApiVersion`/`ResolveOrganizationId`). Explicit parameters always win; missing values fall back to the active connection and emit a `-Verbose` line. diff --git a/Module/PSInfisicalAPI/PSInfisicalAPI.psd1 b/Module/PSInfisicalAPI/PSInfisicalAPI.psd1 index 2d0e294..57b8442 100644 --- a/Module/PSInfisicalAPI/PSInfisicalAPI.psd1 +++ b/Module/PSInfisicalAPI/PSInfisicalAPI.psd1 @@ -1,6 +1,6 @@ @{ RootModule = 'PSInfisicalAPI.psm1' - ModuleVersion = '2026.06.04.0005' + ModuleVersion = '2026.06.04.0020' GUID = 'b8a2f3d4-7c51-4d2f-9e6a-1f0c8b3d4e51' Author = 'Grace Solutions' CompanyName = 'Grace Solutions' @@ -51,7 +51,7 @@ LicenseUri = 'https://www.gnu.org/licenses/agpl-3.0.html' ProjectUri = 'https://prod.git.gracesolution.info/gsadmin/PSInfisicalAPI' ReleaseNotes = 'See CHANGELOG.md in the project repository for release history.' - CommitHash = 'e0a6ef02df3e' + CommitHash = '211fbcf34dbb' } } } \ No newline at end of file diff --git a/Module/PSInfisicalAPI/bin/PSInfisicalAPI.dll b/Module/PSInfisicalAPI/bin/PSInfisicalAPI.dll index c2228bd2b65eb15cce929867338949db1dc619ef..d3eae94af93bdfa9b8663c93b708515b7c9f8bd3 100644 GIT binary patch literal 178688 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 literal 177152 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 z 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 q#dzSAGj=$-k|ax2&Qk=v!= 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 diff --git a/src/PSInfisicalAPI.Tests/BulkSecretConverterTests.cs b/src/PSInfisicalAPI.Tests/BulkSecretConverterTests.cs index 81f3870..c679d20 100644 --- a/src/PSInfisicalAPI.Tests/BulkSecretConverterTests.cs +++ b/src/PSInfisicalAPI.Tests/BulkSecretConverterTests.cs @@ -1,4 +1,8 @@ +using System.Collections; using System.Collections.Generic; +using System.Collections.Specialized; +using System.Management.Automation; +using PSInfisicalAPI.Cmdlets; using PSInfisicalAPI.Errors; using PSInfisicalAPI.Secrets; using Xunit; @@ -90,4 +94,116 @@ namespace PSInfisicalAPI.Tests Assert.Empty(items); } } + + public class BulkSecretsTransformationAttributeTests + { + private static IDictionary[] Transform(object input) + { + BulkSecretsTransformationAttribute attribute = new BulkSecretsTransformationAttribute(); + object result = attribute.Transform(null, input); + return (IDictionary[])result; + } + + [Fact] + public void Transform_Single_Hashtable_Wraps_Into_Array() + { + Hashtable input = new Hashtable { { "SecretName", "API_KEY" }, { "SecretValue", "abc" } }; + + IDictionary[] result = Transform(input); + + Assert.Single(result); + Assert.Equal("API_KEY", result[0]["SecretName"]); + Assert.Equal("abc", result[0]["SecretValue"]); + } + + [Fact] + public void Transform_Hashtable_Array_Preserves_Order() + { + Hashtable a = new Hashtable { { "SecretName", "A" }, { "SecretValue", "1" } }; + Hashtable b = new Hashtable { { "SecretName", "B" }, { "SecretValue", "2" } }; + + IDictionary[] result = Transform(new object[] { a, b }); + + Assert.Equal(2, result.Length); + Assert.Equal("A", result[0]["SecretName"]); + Assert.Equal("B", result[1]["SecretName"]); + } + + [Fact] + public void Transform_OrderedDictionary_Converts_To_StringString() + { + OrderedDictionary input = new OrderedDictionary(); + input.Add("SecretName", "API_KEY"); + input.Add("SkipMultilineEncoding", true); + + IDictionary[] result = Transform(input); + + Assert.Single(result); + Assert.Equal("API_KEY", result[0]["SecretName"]); + Assert.Equal("true", result[0]["SkipMultilineEncoding"]); + } + + [Fact] + public void Transform_Array_Values_Are_Joined_Comma_Separated() + { + Hashtable input = new Hashtable + { + { "SecretName", "API_KEY" }, + { "TagIds", new[] { "tag-1", "tag-2" } } + }; + + IDictionary[] result = Transform(input); + + Assert.Equal("tag-1,tag-2", result[0]["TagIds"]); + } + + [Fact] + public void Transform_Already_Typed_Array_Passes_Through() + { + IDictionary[] input = new IDictionary[] + { + new Dictionary { { "SecretName", "A" }, { "SecretValue", "1" } } + }; + + IDictionary[] result = Transform(input); + + Assert.Same(input, result); + } + + [Fact] + public void Transform_Null_Input_Returns_Null() + { + BulkSecretsTransformationAttribute attribute = new BulkSecretsTransformationAttribute(); + Assert.Null(attribute.Transform(null, null)); + } + + [Fact] + public void Transform_Invalid_Element_Throws_ArgumentTransformationMetadataException() + { + BulkSecretsTransformationAttribute attribute = new BulkSecretsTransformationAttribute(); + Assert.Throws(() => + attribute.Transform(null, new object[] { "not-a-dictionary" })); + } + + [Fact] + public void Transform_End_To_End_With_Converter_Produces_Bulk_Items() + { + Hashtable entry = new Hashtable + { + { "SecretName", "API_KEY" }, + { "SecretValue", "abc" }, + { "TagIds", new[] { "tag-1", "tag-2" } }, + { "SkipMultilineEncoding", true } + }; + + IDictionary[] transformed = Transform(new object[] { entry }); + InfisicalBulkCreateSecretItem[] items = InfisicalBulkSecretConverter.ToCreateItems(transformed); + + Assert.Single(items); + Assert.Equal("API_KEY", items[0].SecretName); + Assert.Equal("abc", items[0].SecretValue); + Assert.Equal(new[] { "tag-1", "tag-2" }, items[0].TagIds); + Assert.True(items[0].SkipMultilineEncoding); + } + } } diff --git a/src/PSInfisicalAPI/Cmdlets/BulkSecretsTransformationAttribute.cs b/src/PSInfisicalAPI/Cmdlets/BulkSecretsTransformationAttribute.cs new file mode 100644 index 0000000..de7e289 --- /dev/null +++ b/src/PSInfisicalAPI/Cmdlets/BulkSecretsTransformationAttribute.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Management.Automation; +using PSInfisicalAPI.Security; + +namespace PSInfisicalAPI.Cmdlets +{ + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] + public sealed class BulkSecretsTransformationAttribute : ArgumentTransformationAttribute + { + public override object Transform(EngineIntrinsics engineIntrinsics, object inputData) + { + if (inputData == null) { return null; } + + object unwrapped = Unwrap(inputData); + + if (unwrapped is IDictionary[] strongArray) { return strongArray; } + + if (unwrapped is IDictionary singleDict) + { + return new IDictionary[] { Convert(singleDict) }; + } + + if (unwrapped is IEnumerable enumerable && !(unwrapped is string)) + { + List> result = new List>(); + foreach (object element in enumerable) + { + if (element == null) { continue; } + object e = Unwrap(element); + if (e is IDictionary dict) + { + result.Add(Convert(dict)); + continue; + } + + throw new ArgumentTransformationMetadataException( + "Each element of -Secrets must be a dictionary (Hashtable, OrderedDictionary, Dictionary, etc.)."); + } + + return result.ToArray(); + } + + throw new ArgumentTransformationMetadataException( + "-Secrets must be a dictionary or an array of dictionaries."); + } + + private static object Unwrap(object value) + { + PSObject pso = value as PSObject; + return pso != null ? pso.BaseObject : value; + } + + private static IDictionary Convert(IDictionary source) + { + Dictionary dict = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (DictionaryEntry entry in source) + { + if (entry.Key == null) { continue; } + string key = entry.Key.ToString(); + dict[key] = Stringify(entry.Value); + } + + return dict; + } + + private static string Stringify(object value) + { + object v = Unwrap(value); + if (v == null) { return null; } + if (v is string s) { return s; } + if (v is bool b) { return b ? "true" : "false"; } + if (v is System.Security.SecureString secure) + { + return SecureStringUtility.UsePlainText(secure, plain => plain); + } + + if (v is IEnumerable enumerable) + { + List parts = new List(); + foreach (object item in enumerable) + { + if (item == null) { continue; } + parts.Add(Unwrap(item).ToString()); + } + + return string.Join(",", parts); + } + + return v.ToString(); + } + } +} diff --git a/src/PSInfisicalAPI/Cmdlets/NewInfisicalSecretCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/NewInfisicalSecretCmdlet.cs index f853073..2d8962a 100644 --- a/src/PSInfisicalAPI/Cmdlets/NewInfisicalSecretCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/NewInfisicalSecretCmdlet.cs @@ -24,6 +24,7 @@ namespace PSInfisicalAPI.Cmdlets public SecureString SecureSecretValue { get; set; } [Parameter(Mandatory = true, Position = 0, ParameterSetName = "Bulk", ValueFromPipeline = true)] + [BulkSecretsTransformation] public IDictionary[] Secrets { get; set; } [Parameter] public string SecretComment { get; set; } diff --git a/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalSecretCmdlet.cs b/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalSecretCmdlet.cs index 9ebd1f6..0449af7 100644 --- a/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalSecretCmdlet.cs +++ b/src/PSInfisicalAPI/Cmdlets/UpdateInfisicalSecretCmdlet.cs @@ -21,6 +21,7 @@ namespace PSInfisicalAPI.Cmdlets [Parameter(ParameterSetName = "SecureString")] public SecureString SecureSecretValue { get; set; } [Parameter(Mandatory = true, Position = 0, ParameterSetName = "Bulk", ValueFromPipeline = true)] + [BulkSecretsTransformation] public IDictionary[] Secrets { get; set; } [Parameter] public string NewSecretName { get; set; } -- 2.52.0