Tests: roll forward to latest major .NET runtime #3
@@ -6,6 +6,25 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) loos
|
|||||||
|
|
||||||
## Unreleased
|
## 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
|
## 2026.06.03.2136
|
||||||
|
|
||||||
- Build produced from commit d9822aab7a4a.
|
- Build produced from commit d9822aab7a4a.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
@{
|
@{
|
||||||
RootModule = 'PSInfisicalAPI.psm1'
|
RootModule = 'PSInfisicalAPI.psm1'
|
||||||
ModuleVersion = '2026.06.03.2136'
|
ModuleVersion = '2026.06.03.2207'
|
||||||
GUID = 'b8a2f3d4-7c51-4d2f-9e6a-1f0c8b3d4e51'
|
GUID = 'b8a2f3d4-7c51-4d2f-9e6a-1f0c8b3d4e51'
|
||||||
Author = 'Grace Solutions'
|
Author = 'Grace Solutions'
|
||||||
CompanyName = 'Grace Solutions'
|
CompanyName = 'Grace Solutions'
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
'New-InfisicalSecret',
|
'New-InfisicalSecret',
|
||||||
'Update-InfisicalSecret',
|
'Update-InfisicalSecret',
|
||||||
'Remove-InfisicalSecret',
|
'Remove-InfisicalSecret',
|
||||||
|
'Copy-InfisicalSecret',
|
||||||
'ConvertTo-InfisicalSecretDictionary',
|
'ConvertTo-InfisicalSecretDictionary',
|
||||||
'Export-InfisicalSecrets',
|
'Export-InfisicalSecrets',
|
||||||
'Get-InfisicalProjects',
|
'Get-InfisicalProjects',
|
||||||
@@ -50,7 +51,7 @@
|
|||||||
LicenseUri = 'https://www.gnu.org/licenses/agpl-3.0.html'
|
LicenseUri = 'https://www.gnu.org/licenses/agpl-3.0.html'
|
||||||
ProjectUri = 'https://prod.git.gracesolution.info/gsadmin/PSInfisicalAPI'
|
ProjectUri = 'https://prod.git.gracesolution.info/gsadmin/PSInfisicalAPI'
|
||||||
ReleaseNotes = 'See CHANGELOG.md in the project repository for release history.'
|
ReleaseNotes = 'See CHANGELOG.md in the project repository for release history.'
|
||||||
CommitHash = 'd9822aab7a4a'
|
CommitHash = '09c3d5c68bbc'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Binary file not shown.
@@ -105,6 +105,7 @@ function Write-Manifest {
|
|||||||
'New-InfisicalSecret',
|
'New-InfisicalSecret',
|
||||||
'Update-InfisicalSecret',
|
'Update-InfisicalSecret',
|
||||||
'Remove-InfisicalSecret',
|
'Remove-InfisicalSecret',
|
||||||
|
'Copy-InfisicalSecret',
|
||||||
'ConvertTo-InfisicalSecretDictionary',
|
'ConvertTo-InfisicalSecretDictionary',
|
||||||
'Export-InfisicalSecrets',
|
'Export-InfisicalSecrets',
|
||||||
'Get-InfisicalProjects',
|
'Get-InfisicalProjects',
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ Get-InfisicalSecret
|
|||||||
New-InfisicalSecret
|
New-InfisicalSecret
|
||||||
Update-InfisicalSecret
|
Update-InfisicalSecret
|
||||||
Remove-InfisicalSecret
|
Remove-InfisicalSecret
|
||||||
|
Copy-InfisicalSecret
|
||||||
ConvertTo-InfisicalSecretDictionary
|
ConvertTo-InfisicalSecretDictionary
|
||||||
Export-InfisicalSecrets
|
Export-InfisicalSecrets
|
||||||
Get-InfisicalProjects
|
Get-InfisicalProjects
|
||||||
@@ -224,6 +225,7 @@ Example shape:
|
|||||||
'New-InfisicalSecret',
|
'New-InfisicalSecret',
|
||||||
'Update-InfisicalSecret',
|
'Update-InfisicalSecret',
|
||||||
'Remove-InfisicalSecret',
|
'Remove-InfisicalSecret',
|
||||||
|
'Copy-InfisicalSecret',
|
||||||
'ConvertTo-InfisicalSecretDictionary',
|
'ConvertTo-InfisicalSecretDictionary',
|
||||||
'Export-InfisicalSecrets',
|
'Export-InfisicalSecrets',
|
||||||
'Get-InfisicalProjects',
|
'Get-InfisicalProjects',
|
||||||
|
|||||||
@@ -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<InfisicalConfigurationException>(() =>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<string> VerboseEntries { get; } = new List<string>();
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -71,6 +71,10 @@ namespace PSInfisicalAPI.Tests
|
|||||||
[InlineData(InfisicalEndpointNames.LdapAuthLogin, "POST", "/api/v1/auth/ldap-auth/login")]
|
[InlineData(InfisicalEndpointNames.LdapAuthLogin, "POST", "/api/v1/auth/ldap-auth/login")]
|
||||||
[InlineData(InfisicalEndpointNames.AzureAuthLogin, "POST", "/api/v1/auth/azure-auth/login")]
|
[InlineData(InfisicalEndpointNames.AzureAuthLogin, "POST", "/api/v1/auth/azure-auth/login")]
|
||||||
[InlineData(InfisicalEndpointNames.GcpIamAuthLogin, "POST", "/api/v1/auth/gcp-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)
|
public void Registered_Endpoints_Have_Expected_Shape(string name, string method, string template)
|
||||||
{
|
{
|
||||||
InfisicalEndpointDefinition definition = InfisicalEndpointRegistry.Get(name);
|
InfisicalEndpointDefinition definition = InfisicalEndpointRegistry.Get(name);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,8 +21,9 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||||
|
string resolvedProjectId = ResolveProjectId(connection, ProjectId);
|
||||||
InfisicalEnvironmentClient client = new InfisicalEnvironmentClient(HttpClient, Logger);
|
InfisicalEnvironmentClient client = new InfisicalEnvironmentClient(HttpClient, Logger);
|
||||||
InfisicalEnvironment env = client.Retrieve(connection, ProjectId, EnvironmentSlugOrId);
|
InfisicalEnvironment env = client.Retrieve(connection, resolvedProjectId, EnvironmentSlugOrId);
|
||||||
if (env != null)
|
if (env != null)
|
||||||
{
|
{
|
||||||
WriteObject(env);
|
WriteObject(env);
|
||||||
|
|||||||
@@ -17,8 +17,9 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||||
|
string resolvedProjectId = ResolveProjectId(connection, ProjectId);
|
||||||
InfisicalEnvironmentClient client = new InfisicalEnvironmentClient(HttpClient, Logger);
|
InfisicalEnvironmentClient client = new InfisicalEnvironmentClient(HttpClient, Logger);
|
||||||
InfisicalEnvironment[] envs = client.List(connection, ProjectId);
|
InfisicalEnvironment[] envs = client.List(connection, resolvedProjectId);
|
||||||
foreach (InfisicalEnvironment env in envs)
|
foreach (InfisicalEnvironment env in envs)
|
||||||
{
|
{
|
||||||
WriteObject(env);
|
WriteObject(env);
|
||||||
|
|||||||
@@ -23,8 +23,11 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
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);
|
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)
|
if (folder != null)
|
||||||
{
|
{
|
||||||
WriteObject(folder);
|
WriteObject(folder);
|
||||||
|
|||||||
@@ -19,8 +19,11 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
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);
|
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)
|
foreach (InfisicalFolder folder in folders)
|
||||||
{
|
{
|
||||||
WriteObject(folder);
|
WriteObject(folder);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
[OutputType(typeof(InfisicalProject))]
|
[OutputType(typeof(InfisicalProject))]
|
||||||
public sealed class GetInfisicalProjectCmdlet : InfisicalCmdletBase
|
public sealed class GetInfisicalProjectCmdlet : InfisicalCmdletBase
|
||||||
{
|
{
|
||||||
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
|
[Parameter(ValueFromPipelineByPropertyName = true, Position = 0)]
|
||||||
[Alias("Id")]
|
[Alias("Id")]
|
||||||
public string ProjectId { get; set; }
|
public string ProjectId { get; set; }
|
||||||
|
|
||||||
@@ -19,8 +19,9 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||||
|
string resolvedProjectId = ResolveProjectId(connection, ProjectId);
|
||||||
InfisicalProjectClient client = new InfisicalProjectClient(HttpClient, Logger);
|
InfisicalProjectClient client = new InfisicalProjectClient(HttpClient, Logger);
|
||||||
InfisicalProject project = client.Retrieve(connection, ProjectId);
|
InfisicalProject project = client.Retrieve(connection, resolvedProjectId);
|
||||||
if (project != null)
|
if (project != null)
|
||||||
{
|
{
|
||||||
WriteObject(project);
|
WriteObject(project);
|
||||||
|
|||||||
@@ -32,10 +32,10 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
InfisicalRetrieveSecretQuery query = new InfisicalRetrieveSecretQuery
|
InfisicalRetrieveSecretQuery query = new InfisicalRetrieveSecretQuery
|
||||||
{
|
{
|
||||||
SecretName = SecretName,
|
SecretName = SecretName,
|
||||||
ProjectId = ProjectId,
|
ProjectId = ResolveProjectId(connection, ProjectId),
|
||||||
Environment = Environment,
|
Environment = ResolveEnvironment(connection, Environment),
|
||||||
SecretPath = SecretPath,
|
SecretPath = ResolveSecretPath(connection, SecretPath),
|
||||||
ApiVersion = ApiVersion,
|
ApiVersion = ResolveApiVersion(connection, ApiVersion),
|
||||||
Version = Version,
|
Version = Version,
|
||||||
Type = Type.ToString(),
|
Type = Type.ToString(),
|
||||||
ViewSecretValue = ViewSecretValue.IsPresent,
|
ViewSecretValue = ViewSecretValue.IsPresent,
|
||||||
|
|||||||
@@ -32,10 +32,10 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
|
|
||||||
InfisicalListSecretsQuery query = new InfisicalListSecretsQuery
|
InfisicalListSecretsQuery query = new InfisicalListSecretsQuery
|
||||||
{
|
{
|
||||||
ProjectId = ProjectId,
|
ProjectId = ResolveProjectId(connection, ProjectId),
|
||||||
Environment = Environment,
|
Environment = ResolveEnvironment(connection, Environment),
|
||||||
SecretPath = SecretPath,
|
SecretPath = ResolveSecretPath(connection, SecretPath),
|
||||||
ApiVersion = ApiVersion,
|
ApiVersion = ResolveApiVersion(connection, ApiVersion),
|
||||||
Recursive = Recursive.IsPresent,
|
Recursive = Recursive.IsPresent,
|
||||||
IncludeImports = IncludeImports.IsPresent,
|
IncludeImports = IncludeImports.IsPresent,
|
||||||
IncludePersonalOverrides = IncludePersonalOverrides.IsPresent,
|
IncludePersonalOverrides = IncludePersonalOverrides.IsPresent,
|
||||||
|
|||||||
@@ -21,8 +21,9 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||||
|
string resolvedProjectId = ResolveProjectId(connection, ProjectId);
|
||||||
InfisicalTagClient client = new InfisicalTagClient(HttpClient, Logger);
|
InfisicalTagClient client = new InfisicalTagClient(HttpClient, Logger);
|
||||||
InfisicalTag tag = client.Retrieve(connection, ProjectId, TagSlugOrId);
|
InfisicalTag tag = client.Retrieve(connection, resolvedProjectId, TagSlugOrId);
|
||||||
if (tag != null)
|
if (tag != null)
|
||||||
{
|
{
|
||||||
WriteObject(tag);
|
WriteObject(tag);
|
||||||
|
|||||||
@@ -17,8 +17,9 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||||
|
string resolvedProjectId = ResolveProjectId(connection, ProjectId);
|
||||||
InfisicalTagClient client = new InfisicalTagClient(HttpClient, Logger);
|
InfisicalTagClient client = new InfisicalTagClient(HttpClient, Logger);
|
||||||
InfisicalTag[] tags = client.List(connection, ProjectId);
|
InfisicalTag[] tags = client.List(connection, resolvedProjectId);
|
||||||
foreach (InfisicalTag tag in tags)
|
foreach (InfisicalTag tag in tags)
|
||||||
{
|
{
|
||||||
WriteObject(tag);
|
WriteObject(tag);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Management.Automation;
|
using System.Management.Automation;
|
||||||
|
using PSInfisicalAPI.Connections;
|
||||||
using PSInfisicalAPI.Errors;
|
using PSInfisicalAPI.Errors;
|
||||||
using PSInfisicalAPI.Http;
|
using PSInfisicalAPI.Http;
|
||||||
using PSInfisicalAPI.Logging;
|
using PSInfisicalAPI.Logging;
|
||||||
@@ -44,5 +45,43 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
ErrorRecord record = InfisicalErrorHandler.ToErrorRecord(exception, details);
|
ErrorRecord record = InfisicalErrorHandler.ToErrorRecord(exception, details);
|
||||||
ThrowTerminatingError(record);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,8 +25,9 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
}
|
}
|
||||||
|
|
||||||
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||||
|
string resolvedProjectId = ResolveProjectId(connection, ProjectId);
|
||||||
InfisicalEnvironmentClient client = new InfisicalEnvironmentClient(HttpClient, Logger);
|
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)
|
if (env != null)
|
||||||
{
|
{
|
||||||
WriteObject(env);
|
WriteObject(env);
|
||||||
|
|||||||
@@ -25,8 +25,11 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
}
|
}
|
||||||
|
|
||||||
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
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);
|
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)
|
if (folder != null)
|
||||||
{
|
{
|
||||||
WriteObject(folder);
|
WriteObject(folder);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Management.Automation;
|
using System.Management.Automation;
|
||||||
using System.Security;
|
using System.Security;
|
||||||
using PSInfisicalAPI.Connections;
|
using PSInfisicalAPI.Connections;
|
||||||
@@ -12,7 +13,9 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
[OutputType(typeof(InfisicalSecret))]
|
[OutputType(typeof(InfisicalSecret))]
|
||||||
public sealed class NewInfisicalSecretCmdlet : InfisicalCmdletBase
|
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")]
|
[Parameter(Mandatory = true, Position = 1, ParameterSetName = "PlainText")]
|
||||||
public string SecretValue { get; set; }
|
public string SecretValue { get; set; }
|
||||||
@@ -20,6 +23,9 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
[Parameter(Mandatory = true, Position = 1, ParameterSetName = "SecureString")]
|
[Parameter(Mandatory = true, Position = 1, ParameterSetName = "SecureString")]
|
||||||
public SecureString SecureSecretValue { get; set; }
|
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 SecretComment { get; set; }
|
||||||
[Parameter] public string ProjectId { get; set; }
|
[Parameter] public string ProjectId { get; set; }
|
||||||
[Parameter] public string Environment { get; set; }
|
[Parameter] public string Environment { get; set; }
|
||||||
@@ -33,35 +39,62 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
{
|
{
|
||||||
try
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!ShouldProcess(SecretName, "Create Infisical secret")) { return; }
|
||||||
|
|
||||||
string plainValue = SecureSecretValue != null
|
string plainValue = SecureSecretValue != null
|
||||||
? SecureStringUtility.UsePlainText(SecureSecretValue, p => p)
|
? SecureStringUtility.UsePlainText(SecureSecretValue, p => p)
|
||||||
: SecretValue;
|
: SecretValue;
|
||||||
|
|
||||||
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
|
||||||
InfisicalCreateSecretRequest request = new InfisicalCreateSecretRequest
|
InfisicalCreateSecretRequest request = new InfisicalCreateSecretRequest
|
||||||
{
|
{
|
||||||
SecretName = SecretName,
|
SecretName = SecretName,
|
||||||
SecretValue = plainValue,
|
SecretValue = plainValue,
|
||||||
SecretComment = SecretComment,
|
SecretComment = SecretComment,
|
||||||
ProjectId = ProjectId,
|
ProjectId = resolvedProjectId,
|
||||||
Environment = Environment,
|
Environment = resolvedEnvironment,
|
||||||
SecretPath = SecretPath,
|
SecretPath = resolvedSecretPath,
|
||||||
Type = Type.ToString(),
|
Type = Type.ToString(),
|
||||||
ApiVersion = ApiVersion,
|
ApiVersion = resolvedApiVersion,
|
||||||
SkipMultilineEncoding = SkipMultilineEncoding.IsPresent ? (bool?)true : null,
|
SkipMultilineEncoding = SkipMultilineEncoding.IsPresent ? (bool?)true : null,
|
||||||
TagIds = TagIds
|
TagIds = TagIds
|
||||||
};
|
};
|
||||||
|
|
||||||
InfisicalSecretsClient client = new InfisicalSecretsClient(HttpClient, Logger);
|
InfisicalSecretsClient client = new InfisicalSecretsClient(HttpClient, Logger);
|
||||||
InfisicalSecret secret = client.Create(connection, request);
|
InfisicalSecret single = client.Create(connection, request);
|
||||||
if (secret != null)
|
if (single != null)
|
||||||
{
|
{
|
||||||
WriteObject(secret);
|
WriteObject(single);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
|
|||||||
@@ -25,8 +25,9 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
}
|
}
|
||||||
|
|
||||||
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||||
|
string resolvedProjectId = ResolveProjectId(connection, ProjectId);
|
||||||
InfisicalTagClient client = new InfisicalTagClient(HttpClient, Logger);
|
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)
|
if (tag != null)
|
||||||
{
|
{
|
||||||
WriteObject(tag);
|
WriteObject(tag);
|
||||||
|
|||||||
@@ -25,8 +25,9 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
}
|
}
|
||||||
|
|
||||||
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||||
|
string resolvedProjectId = ResolveProjectId(connection, ProjectId);
|
||||||
InfisicalEnvironmentClient client = new InfisicalEnvironmentClient(HttpClient, Logger);
|
InfisicalEnvironmentClient client = new InfisicalEnvironmentClient(HttpClient, Logger);
|
||||||
client.Delete(connection, ProjectId, EnvironmentId);
|
client.Delete(connection, resolvedProjectId, EnvironmentId);
|
||||||
|
|
||||||
if (PassThru.IsPresent)
|
if (PassThru.IsPresent)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,8 +27,11 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
}
|
}
|
||||||
|
|
||||||
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
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);
|
InfisicalFolderClient client = new InfisicalFolderClient(HttpClient, Logger);
|
||||||
client.Delete(connection, ProjectId, Environment, FolderId, Path);
|
client.Delete(connection, resolvedProjectId, resolvedEnvironment, FolderId, resolvedPath);
|
||||||
|
|
||||||
if (PassThru.IsPresent)
|
if (PassThru.IsPresent)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
[Cmdlet(VerbsCommon.Remove, "InfisicalProject", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)]
|
[Cmdlet(VerbsCommon.Remove, "InfisicalProject", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)]
|
||||||
public sealed class RemoveInfisicalProjectCmdlet : InfisicalCmdletBase
|
public sealed class RemoveInfisicalProjectCmdlet : InfisicalCmdletBase
|
||||||
{
|
{
|
||||||
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
|
[Parameter(ValueFromPipelineByPropertyName = true, Position = 0)]
|
||||||
[Alias("Id")]
|
[Alias("Id")]
|
||||||
public string ProjectId { get; set; }
|
public string ProjectId { get; set; }
|
||||||
|
|
||||||
@@ -18,18 +18,20 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!ShouldProcess(ProjectId, "Remove Infisical project"))
|
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||||
|
string resolvedProjectId = ResolveProjectId(connection, ProjectId);
|
||||||
|
|
||||||
|
if (!ShouldProcess(resolvedProjectId, "Remove Infisical project"))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
|
||||||
InfisicalProjectClient client = new InfisicalProjectClient(HttpClient, Logger);
|
InfisicalProjectClient client = new InfisicalProjectClient(HttpClient, Logger);
|
||||||
client.Delete(connection, ProjectId);
|
client.Delete(connection, resolvedProjectId);
|
||||||
|
|
||||||
if (PassThru.IsPresent)
|
if (PassThru.IsPresent)
|
||||||
{
|
{
|
||||||
WriteObject(ProjectId);
|
WriteObject(resolvedProjectId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
|
|||||||
@@ -6,12 +6,16 @@ using PSInfisicalAPI.Secrets;
|
|||||||
|
|
||||||
namespace PSInfisicalAPI.Cmdlets
|
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
|
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; }
|
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 ProjectId { get; set; }
|
||||||
[Parameter] public string Environment { get; set; }
|
[Parameter] public string Environment { get; set; }
|
||||||
[Parameter] public string SecretPath { get; set; }
|
[Parameter] public string SecretPath { get; set; }
|
||||||
@@ -23,23 +27,51 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
{
|
{
|
||||||
try
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
if (!ShouldProcess(SecretName, "Remove Infisical secret")) { return; }
|
||||||
|
|
||||||
InfisicalDeleteSecretRequest request = new InfisicalDeleteSecretRequest
|
InfisicalDeleteSecretRequest request = new InfisicalDeleteSecretRequest
|
||||||
{
|
{
|
||||||
SecretName = SecretName,
|
SecretName = SecretName,
|
||||||
ProjectId = ProjectId,
|
ProjectId = resolvedProjectId,
|
||||||
Environment = Environment,
|
Environment = resolvedEnvironment,
|
||||||
SecretPath = SecretPath,
|
SecretPath = resolvedSecretPath,
|
||||||
Type = Type.ToString(),
|
Type = Type.ToString(),
|
||||||
ApiVersion = ApiVersion
|
ApiVersion = resolvedApiVersion
|
||||||
};
|
};
|
||||||
|
|
||||||
InfisicalSecretsClient client = new InfisicalSecretsClient(HttpClient, Logger);
|
|
||||||
client.Delete(connection, request);
|
client.Delete(connection, request);
|
||||||
|
|
||||||
if (PassThru.IsPresent)
|
if (PassThru.IsPresent)
|
||||||
|
|||||||
@@ -25,8 +25,9 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
}
|
}
|
||||||
|
|
||||||
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||||
|
string resolvedProjectId = ResolveProjectId(connection, ProjectId);
|
||||||
InfisicalTagClient client = new InfisicalTagClient(HttpClient, Logger);
|
InfisicalTagClient client = new InfisicalTagClient(HttpClient, Logger);
|
||||||
client.Delete(connection, ProjectId, TagId);
|
client.Delete(connection, resolvedProjectId, TagId);
|
||||||
|
|
||||||
if (PassThru.IsPresent)
|
if (PassThru.IsPresent)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -29,8 +29,9 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
}
|
}
|
||||||
|
|
||||||
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||||
|
string resolvedProjectId = ResolveProjectId(connection, ProjectId);
|
||||||
InfisicalEnvironmentClient client = new InfisicalEnvironmentClient(HttpClient, Logger);
|
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)
|
if (env != null)
|
||||||
{
|
{
|
||||||
WriteObject(env);
|
WriteObject(env);
|
||||||
|
|||||||
@@ -29,8 +29,11 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
}
|
}
|
||||||
|
|
||||||
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
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);
|
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)
|
if (folder != null)
|
||||||
{
|
{
|
||||||
WriteObject(folder);
|
WriteObject(folder);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
[OutputType(typeof(InfisicalProject))]
|
[OutputType(typeof(InfisicalProject))]
|
||||||
public sealed class UpdateInfisicalProjectCmdlet : InfisicalCmdletBase
|
public sealed class UpdateInfisicalProjectCmdlet : InfisicalCmdletBase
|
||||||
{
|
{
|
||||||
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position = 0)]
|
[Parameter(ValueFromPipelineByPropertyName = true, Position = 0)]
|
||||||
[Alias("Id")]
|
[Alias("Id")]
|
||||||
public string ProjectId { get; set; }
|
public string ProjectId { get; set; }
|
||||||
|
|
||||||
@@ -22,14 +22,16 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!ShouldProcess(ProjectId, "Update Infisical project"))
|
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||||
|
string resolvedProjectId = ResolveProjectId(connection, ProjectId);
|
||||||
|
|
||||||
|
if (!ShouldProcess(resolvedProjectId, "Update Infisical project"))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
|
||||||
InfisicalProjectClient client = new InfisicalProjectClient(HttpClient, Logger);
|
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)
|
if (project != null)
|
||||||
{
|
{
|
||||||
WriteObject(project);
|
WriteObject(project);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Management.Automation;
|
using System.Management.Automation;
|
||||||
using System.Security;
|
using System.Security;
|
||||||
using PSInfisicalAPI.Connections;
|
using PSInfisicalAPI.Connections;
|
||||||
@@ -12,12 +13,16 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
[OutputType(typeof(InfisicalSecret))]
|
[OutputType(typeof(InfisicalSecret))]
|
||||||
public sealed class UpdateInfisicalSecretCmdlet : InfisicalCmdletBase
|
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; }
|
public string SecretName { get; set; }
|
||||||
|
|
||||||
[Parameter(ParameterSetName = "PlainText")] public string SecretValue { get; set; }
|
[Parameter(ParameterSetName = "PlainText")] public string SecretValue { get; set; }
|
||||||
[Parameter(ParameterSetName = "SecureString")] public SecureString SecureSecretValue { 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 NewSecretName { get; set; }
|
||||||
[Parameter] public string SecretComment { get; set; }
|
[Parameter] public string SecretComment { get; set; }
|
||||||
[Parameter] public string ProjectId { get; set; }
|
[Parameter] public string ProjectId { get; set; }
|
||||||
@@ -32,36 +37,63 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
{
|
{
|
||||||
try
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!ShouldProcess(SecretName, "Update Infisical secret")) { return; }
|
||||||
|
|
||||||
string plainValue = SecureSecretValue != null
|
string plainValue = SecureSecretValue != null
|
||||||
? SecureStringUtility.UsePlainText(SecureSecretValue, p => p)
|
? SecureStringUtility.UsePlainText(SecureSecretValue, p => p)
|
||||||
: SecretValue;
|
: SecretValue;
|
||||||
|
|
||||||
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
|
||||||
InfisicalUpdateSecretRequest request = new InfisicalUpdateSecretRequest
|
InfisicalUpdateSecretRequest request = new InfisicalUpdateSecretRequest
|
||||||
{
|
{
|
||||||
SecretName = SecretName,
|
SecretName = SecretName,
|
||||||
NewSecretName = NewSecretName,
|
NewSecretName = NewSecretName,
|
||||||
SecretValue = plainValue,
|
SecretValue = plainValue,
|
||||||
SecretComment = SecretComment,
|
SecretComment = SecretComment,
|
||||||
ProjectId = ProjectId,
|
ProjectId = resolvedProjectId,
|
||||||
Environment = Environment,
|
Environment = resolvedEnvironment,
|
||||||
SecretPath = SecretPath,
|
SecretPath = resolvedSecretPath,
|
||||||
Type = Type.ToString(),
|
Type = Type.ToString(),
|
||||||
ApiVersion = ApiVersion,
|
ApiVersion = resolvedApiVersion,
|
||||||
SkipMultilineEncoding = SkipMultilineEncoding.IsPresent ? (bool?)true : null,
|
SkipMultilineEncoding = SkipMultilineEncoding.IsPresent ? (bool?)true : null,
|
||||||
TagIds = TagIds
|
TagIds = TagIds
|
||||||
};
|
};
|
||||||
|
|
||||||
InfisicalSecretsClient client = new InfisicalSecretsClient(HttpClient, Logger);
|
InfisicalSecretsClient client = new InfisicalSecretsClient(HttpClient, Logger);
|
||||||
InfisicalSecret secret = client.Update(connection, request);
|
InfisicalSecret single = client.Update(connection, request);
|
||||||
if (secret != null)
|
if (single != null)
|
||||||
{
|
{
|
||||||
WriteObject(secret);
|
WriteObject(single);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
|
|||||||
@@ -29,8 +29,9 @@ namespace PSInfisicalAPI.Cmdlets
|
|||||||
}
|
}
|
||||||
|
|
||||||
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
InfisicalConnection connection = InfisicalSessionManager.RequireCurrent();
|
||||||
|
string resolvedProjectId = ResolveProjectId(connection, ProjectId);
|
||||||
InfisicalTagClient client = new InfisicalTagClient(HttpClient, Logger);
|
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)
|
if (tag != null)
|
||||||
{
|
{
|
||||||
WriteObject(tag);
|
WriteObject(tag);
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ namespace PSInfisicalAPI.Endpoints
|
|||||||
public const string CreateSecret = "CreateSecret";
|
public const string CreateSecret = "CreateSecret";
|
||||||
public const string UpdateSecret = "UpdateSecret";
|
public const string UpdateSecret = "UpdateSecret";
|
||||||
public const string DeleteSecret = "DeleteSecret";
|
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 ListProjects = "ListProjects";
|
||||||
public const string RetrieveProject = "RetrieveProject";
|
public const string RetrieveProject = "RetrieveProject";
|
||||||
|
|||||||
@@ -197,6 +197,54 @@ namespace PSInfisicalAPI.Endpoints
|
|||||||
RequiresAuthorization = true,
|
RequiresAuthorization = true,
|
||||||
ContainsSecretMaterialInResponse = 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<string, List<InfisicalEndpointDefinition>> map)
|
private static void RegisterProjects(Dictionary<string, List<InfisicalEndpointDefinition>> map)
|
||||||
|
|||||||
@@ -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<InfisicalBulkCreateSecretItem> list = new List<InfisicalBulkCreateSecretItem>();
|
||||||
|
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<InfisicalBulkUpdateSecretItem> list = new List<InfisicalBulkUpdateSecretItem>();
|
||||||
|
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<string> items = new List<string>();
|
||||||
|
foreach (object item in enumerable) { if (item != null) { items.Add(item.ToString()); } }
|
||||||
|
return items.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new[] { value.ToString() };
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Dictionary<string, string> 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<string, string> result = new Dictionary<string, string>(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -88,4 +88,75 @@ namespace PSInfisicalAPI.Secrets
|
|||||||
[JsonProperty("secretPath", NullValueHandling = NullValueHandling.Ignore)] public string SecretPath { get; set; }
|
[JsonProperty("secretPath", NullValueHandling = NullValueHandling.Ignore)] public string SecretPath { get; set; }
|
||||||
[JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)] public string Type { 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<InfisicalSecretMetadataDto> 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<InfisicalSecretMetadataDto> 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<InfisicalSecretBatchCreateItemDto> 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<InfisicalSecretBatchUpdateItemDto> 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<InfisicalSecretBatchDeleteItemDto> 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; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,4 +69,69 @@ namespace PSInfisicalAPI.Secrets
|
|||||||
public string Type { get; set; }
|
public string Type { get; set; }
|
||||||
public string ApiVersion { 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<string, string> 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<string, string> 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; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<InfisicalSecretBatchCreateItemDto> items = new List<InfisicalSecretBatchCreateItemDto>(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<InfisicalSecretListResponseDto>(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<InfisicalSecretBatchUpdateItemDto> items = new List<InfisicalSecretBatchUpdateItemDto>(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<InfisicalSecretListResponseDto>(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<InfisicalSecretBatchDeleteItemDto> items = new List<InfisicalSecretBatchDeleteItemDto>(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<InfisicalSecretListResponseDto>(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<InfisicalSecretMetadataDto> ToMetadataDtoList(Dictionary<string, string> metadata)
|
||||||
|
{
|
||||||
|
if (metadata == null || metadata.Count == 0) { return null; }
|
||||||
|
List<InfisicalSecretMetadataDto> list = new List<InfisicalSecretMetadataDto>(metadata.Count);
|
||||||
|
foreach (KeyValuePair<string, string> kvp in metadata)
|
||||||
|
{
|
||||||
|
list.Add(new InfisicalSecretMetadataDto { Key = kvp.Key, Value = kvp.Value });
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
public void Delete(InfisicalConnection connection, InfisicalDeleteSecretRequest request)
|
public void Delete(InfisicalConnection connection, InfisicalDeleteSecretRequest request)
|
||||||
{
|
{
|
||||||
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
|
if (connection == null) { throw new ArgumentNullException(nameof(connection)); }
|
||||||
|
|||||||
Reference in New Issue
Block a user