Skip to content

Commit 57271cb

Browse files
committed
gracefully handle FileTransferProtocol value that is a flag combination
Signed-off-by: Matthew H. Irby <[email protected]>
1 parent 9f47561 commit 57271cb

File tree

11 files changed

+208
-6
lines changed

11 files changed

+208
-6
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using Xunit;
2+
using Keyfactor.Extensions.Orchestrator.RemoteFile;
3+
4+
namespace RemoteFile.UnitTests;
5+
6+
public class ApplicationSettingsTests
7+
{
8+
[Fact]
9+
public void FileTransferProtocol_WhenPopulatedWithValidValue_ReturnsValue()
10+
{
11+
var path = Path.Combine(Directory.GetCurrentDirectory(), "fixtures", "config", "valid", "config.json");
12+
ApplicationSettings.Initialize(path);
13+
Assert.Equal(ApplicationSettings.FileTransferProtocolEnum.SCP, ApplicationSettings.FileTransferProtocol);
14+
}
15+
16+
[Fact]
17+
public void FileTransferProtocol_WhenAllThreePopulated_DefaultsToBoth()
18+
{
19+
var path = Path.Combine(Directory.GetCurrentDirectory(), "fixtures", "config", "file_transfer_protocol_all_three", "config.json");
20+
ApplicationSettings.Initialize(path);
21+
Assert.Equal(ApplicationSettings.FileTransferProtocolEnum.Both, ApplicationSettings.FileTransferProtocol);
22+
}
23+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using Keyfactor.Extensions.Orchestrator.RemoteFile;
2+
using Xunit;
3+
4+
namespace RemoteFile.UnitTests;
5+
6+
public class PropertyUtilitiesTests
7+
{
8+
[Theory]
9+
[InlineData("SCP", ApplicationSettings.FileTransferProtocolEnum.SCP)]
10+
[InlineData("SFTP", ApplicationSettings.FileTransferProtocolEnum.SFTP)]
11+
[InlineData("Both", ApplicationSettings.FileTransferProtocolEnum.Both)]
12+
public void TryEnumParse_WhenProvidedAValidEnumString_MapsToExpectedEnumValue(string input,
13+
ApplicationSettings.FileTransferProtocolEnum expected)
14+
{
15+
var isValid = PropertyUtilities.TryEnumParse(input, out var isFlagCombination,
16+
out ApplicationSettings.FileTransferProtocolEnum result);
17+
18+
Assert.True(isValid);
19+
Assert.Equal(expected, result);
20+
Assert.False(isFlagCombination);
21+
}
22+
23+
[Fact]
24+
public void TryEnumParse_WhenProvidedAFlagCombination_SetsIsFlagCombination()
25+
{
26+
var input = "SCP,SFTP,Both";
27+
28+
var isValid = PropertyUtilities.TryEnumParse(input, out var isFlagCombination,
29+
out ApplicationSettings.FileTransferProtocolEnum result);
30+
31+
Assert.True(isValid);
32+
Assert.Equal((ApplicationSettings.FileTransferProtocolEnum) 3, result);
33+
Assert.True(isFlagCombination);
34+
}
35+
36+
[Fact]
37+
public void TryEnumParse_WhenProvidedAnInvalidMapping_MarksIsValidAsFalse()
38+
{
39+
var input = "randomstring";
40+
41+
var isValid = PropertyUtilities.TryEnumParse(input, out var isFlagCombination,
42+
out ApplicationSettings.FileTransferProtocolEnum result);
43+
44+
Assert.False(isValid);
45+
Assert.Equal(ApplicationSettings.FileTransferProtocolEnum.SCP, result);
46+
Assert.False(isFlagCombination);
47+
}
48+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net6.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
8+
<IsPackable>false</IsPackable>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0"/>
13+
<PackageReference Include="xunit" Version="2.4.1"/>
14+
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
15+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
16+
<PrivateAssets>all</PrivateAssets>
17+
</PackageReference>
18+
<PackageReference Include="coverlet.collector" Version="3.1.2">
19+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
20+
<PrivateAssets>all</PrivateAssets>
21+
</PackageReference>
22+
</ItemGroup>
23+
24+
<ItemGroup>
25+
<ProjectReference Include="..\RemoteFile\RemoteFile.csproj" />
26+
</ItemGroup>
27+
28+
<ItemGroup>
29+
<None Update="fixtures\config\valid\config.json">
30+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
31+
</None>
32+
<None Update="fixtures\config\file_transfer_protocol_all_three\config.json">
33+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
34+
</None>
35+
<None Update="fixtures\config\valid_scp\config.json">
36+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
37+
</None>
38+
</ItemGroup>
39+
40+
</Project>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"UseSudo": "N",
3+
"DefaultSudoImpersonatedUser": "",
4+
"CreateStoreIfMissing": "N",
5+
"UseNegotiate": "N",
6+
"SeparateUploadFilePath": "",
7+
"FileTransferProtocol": "Both,SCP,SFTP",
8+
"DefaultLinuxPermissionsOnStoreCreation": "600",
9+
"DefaultOwnerOnStoreCreation": "",
10+
"SSHPort": ""
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"UseSudo": "N",
3+
"DefaultSudoImpersonatedUser": "",
4+
"CreateStoreIfMissing": "N",
5+
"UseNegotiate": "N",
6+
"SeparateUploadFilePath": "",
7+
"FileTransferProtocol": "SCP",
8+
"DefaultLinuxPermissionsOnStoreCreation": "600",
9+
"DefaultOwnerOnStoreCreation": "",
10+
"SSHPort": ""
11+
}

RemoteFile.sln

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ VisualStudioVersion = 16.0.31702.278
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RemoteFile", "RemoteFile\RemoteFile.csproj", "{A006BFAB-20F7-4F42-8B5F-591268ACE836}"
77
EndProject
8+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{856DF77E-EB78-45EB-836F-50C3B017B4C2}"
9+
EndProject
10+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RemoteFile.UnitTests", "RemoteFile.UnitTests\RemoteFile.UnitTests.csproj", "{2769EBA9-6C62-4409-B637-FFA86E23749E}"
11+
EndProject
812
Global
913
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1014
Debug|Any CPU = Debug|Any CPU
@@ -15,11 +19,18 @@ Global
1519
{A006BFAB-20F7-4F42-8B5F-591268ACE836}.Debug|Any CPU.Build.0 = Debug|Any CPU
1620
{A006BFAB-20F7-4F42-8B5F-591268ACE836}.Release|Any CPU.ActiveCfg = Release|Any CPU
1721
{A006BFAB-20F7-4F42-8B5F-591268ACE836}.Release|Any CPU.Build.0 = Release|Any CPU
22+
{2769EBA9-6C62-4409-B637-FFA86E23749E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23+
{2769EBA9-6C62-4409-B637-FFA86E23749E}.Debug|Any CPU.Build.0 = Debug|Any CPU
24+
{2769EBA9-6C62-4409-B637-FFA86E23749E}.Release|Any CPU.ActiveCfg = Release|Any CPU
25+
{2769EBA9-6C62-4409-B637-FFA86E23749E}.Release|Any CPU.Build.0 = Release|Any CPU
1826
EndGlobalSection
1927
GlobalSection(SolutionProperties) = preSolution
2028
HideSolutionNode = FALSE
2129
EndGlobalSection
2230
GlobalSection(ExtensibilityGlobals) = postSolution
2331
SolutionGuid = {8F3245C7-FCC9-4666-99E0-F8D63BBE8373}
2432
EndGlobalSection
33+
GlobalSection(NestedProjects) = preSolution
34+
{2769EBA9-6C62-4409-B637-FFA86E23749E} = {856DF77E-EB78-45EB-836F-50C3B017B4C2}
35+
EndGlobalSection
2536
EndGlobal

RemoteFile/ApplicationSettings.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
namespace Keyfactor.Extensions.Orchestrator.RemoteFile
1818
{
19-
class ApplicationSettings
19+
public class ApplicationSettings
2020
{
2121
public enum FileTransferProtocolEnum
2222
{
@@ -63,15 +63,27 @@ public static FileTransferProtocolEnum FileTransferProtocol
6363
{
6464
get
6565
{
66+
ILogger logger = LogHandler.GetClassLogger<ApplicationSettings>();
67+
6668
string protocolNames = string.Empty;
6769
foreach (string protocolName in Enum.GetNames(typeof(FileTransferProtocolEnum)))
6870
{
6971
protocolNames += protocolName + ", ";
7072
}
7173
protocolNames = protocolNames.Substring(0, protocolNames.Length - 2);
74+
string? protocolValue = configuration["FileTransferProtocol"];
7275

73-
if (!Enum.TryParse(configuration["FileTransferProtocol"], out FileTransferProtocolEnum protocol))
74-
throw new RemoteFileException($"Invalid optional config.json FileTransferProtocol option of {configuration["FileTransferProtocol"]}. If present, must be one of these values: {protocolNames}.");
76+
if (!PropertyUtilities.TryEnumParse(protocolValue, out bool isFlagCombination, out FileTransferProtocolEnum protocol))
77+
throw new RemoteFileException($"Invalid optional config.json FileTransferProtocol option of {protocolValue}. If present, must be one of these values: {protocolNames}.");
78+
79+
// Issue: If received a comma-delimited list ("SCP,SFTP,Both"), it's treating it as a flag combination (i.e. mapping it to 0+1+2=3)
80+
// If this happens, we want to default it to Both so it's resolved as a valid mapping.
81+
if (isFlagCombination)
82+
{
83+
logger.LogWarning($"FileTransferProtocol config value {protocolValue} mapped to a flag combination. Setting FileTransferProtocol explicitly to Both.");
84+
protocol = FileTransferProtocolEnum.Both;
85+
}
86+
7587
return protocol;
7688
}
7789
}

RemoteFile/ImplementedStoreTypes/PEM/PEMCertificateStoreSerializer.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,9 @@ private void LoadCustomProperties(string storeProperties)
200200
IncludesChain = properties.IncludesChain == null || string.IsNullOrEmpty(properties.IncludesChain.Value) ? false : bool.Parse(properties.IncludesChain.Value);
201201
SeparatePrivateKeyFilePath = properties.SeparatePrivateKeyFilePath == null || string.IsNullOrEmpty(properties.SeparatePrivateKeyFilePath.Value) ? String.Empty : properties.SeparatePrivateKeyFilePath.Value;
202202
IgnorePrivateKeyOnInventory = properties.IgnorePrivateKeyOnInventory == null || string.IsNullOrEmpty(properties.IgnorePrivateKeyOnInventory.Value) ? false : bool.Parse(properties.IgnorePrivateKeyOnInventory.Value);
203+
204+
logger.LogDebug("Custom Properties have been loaded:");
205+
logger.LogDebug($"IsTrustStore: {IsTrustStore}, IncludesChain: {IncludesChain}, SeparatePrivateKeyFilePath: {SeparatePrivateKeyFilePath}, IgnorePrivateKeyOnInventory: {IgnorePrivateKeyOnInventory}");
203206

204207
logger.MethodExit(LogLevel.Debug);
205208
}

RemoteFile/PropertyUtilities.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System;
2+
3+
namespace Keyfactor.Extensions.Orchestrator.RemoteFile;
4+
5+
public static class PropertyUtilities
6+
{
7+
public static bool TryEnumParse<T>(string value, out bool isFlagCombination, out T result) where T : struct, Enum
8+
{
9+
isFlagCombination = false;
10+
result = default(T);
11+
12+
// First, do the normal TryParse
13+
if (!Enum.TryParse<T>(value, out result))
14+
{
15+
return false;
16+
}
17+
18+
// Check if the enum has the Flags attribute
19+
bool hasFlags = typeof(T).GetCustomAttributes(typeof(FlagsAttribute), false).Length > 0;
20+
21+
// If it doesn't have Flags attribute but the input contains commas,
22+
// this might be unintended flag parsing
23+
if (!hasFlags && value.Contains(','))
24+
{
25+
// Check if the parsed result corresponds to a defined enum value
26+
if (!Enum.IsDefined(typeof(T), result))
27+
{
28+
isFlagCombination = true;
29+
return true;
30+
}
31+
}
32+
33+
return true;
34+
}
35+
}

RemoteFile/RemoteCertificateStore.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ internal void CreateCertificateStore(ICertificateStoreSerializer certificateStor
252252
internal void AddCertificate(string alias, string certificateEntry, bool overwrite, string pfxPassword, bool removeRootCertificate)
253253
{
254254
logger.MethodEntry(LogLevel.Debug);
255+
logger.LogDebug($"Alias: {alias}, Certificate Entry: {certificateEntry}, Overwrite: {overwrite}, RemoveRootCertificate: {removeRootCertificate}");
255256

256257
try
257258
{

0 commit comments

Comments
 (0)