Skip to content

Commit fdfb502

Browse files
authored
Fixes ab#47259
* Fixed issue with additional_emails field not syncing. * Added independent logging via NLog.
1 parent 256a8e0 commit fdfb502

17 files changed

+481
-556
lines changed

CHANGELOG.md

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
1+
Version 2.0.2
2+
3+
Fixed issue with additional_emails field not syncing.
4+
Added independent logging via NLog.
5+
16
Version 2.0.1
2-
- Fixed issue with no input for either custom or manual fields leading to a crash.
3-
- Fixed issue with data for imported DigiCert fields renamed with a replacement character not syncing back to DigiCert.
4-
- Fixed possible crash caused by importing DigiCert custom fields with "Anything" data type.
7+
8+
Fixed issue with no input for either custom or manual fields leading to a crash.
9+
Fixed issue with data for imported DigiCert fields renamed with a replacement character not syncing back to DigiCert.
10+
Fixed possible crash caused by importing DigiCert custom fields with "Anything" data type.
511

612
Version 2.0.0
713

8-
- Added ability to sync custom fields from Keyfactor to DigiCert.
9-
- Tool now requires command line argument to specify sync direction: "dctokf" for DigiCert to Keyfactor and "kftodc" for Keyfactor to DigiCert.
10-
- New DigiCert API Key with restrictions set to "None" in DigiCert config required to perform sync from Keyfactor to Digicert.
14+
Added ability to sync custom fields from Keyfactor to DigiCert.
15+
Tool now requires command line argument to specify sync direction: "dctokf" for DigiCert to Keyfactor and "kftodc" for Keyfactor to DigiCert.
16+
New DigiCert API Key with restrictions set to "None" in DigiCert config required to perform sync from Keyfactor to Digicert.
1117

1218
Version 1.0
1319

14-
- Initial Release
20+
Initial Release

README.md

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ A tool to automatically synchronize metadata fields and their content from DigiC
44

55
#### Integration status: Production - Ready for use in production environments.
66

7-
## About the Keyfactor API Client
87

9-
This API client allows for programmatic management of Keyfactor resources.
108

119

1210

@@ -19,30 +17,19 @@ Digicert Metadata Sync is open source and there is **no SLA** for this tool/libr
1917

2018

2119

22-
23-
2420
## Overview
25-
This tool primarily sets up metadata fields in Keyfactor for the custom metadata fields in DigiCert, which are named as such, but can also setup metadata fields in Keyfactor for non-custom fields available in DigiCert and unavailable in Keyfactor by default, such as the Digicert Cert ID and the Organization contact. These fields are referred to as manual fields in the context of this tool. After setting up these fields, the tool proceeds to update the contents of these fields. This tool only adds metadata to certificates that have already been imported into Keyfactor. Additionally, this tool requires a properly installed and functioning AnyGateway configured to work with Keyfactor and Digicert. The latest update allows for syncronization of custom field contents from Keyfactor to DigiCert. New fields are created in Keyfactor and DigiCert to accomodate for this.
21+
This tool primarily sets up metadata fields in Keyfactor for the custom metadata fields in DigiCert, which are named as such, but can also setup metadata fields in Keyfactor for non-custom fields available in DigiCert and unavailable in Keyfactor by default, such as the Digicert Cert ID and the Organization contact. These fields are referred to as manual fields in the context of this tool. After setting up these fields, the tool proceeds to update the contents of these fields. This tool only adds metadata to certificates that have already been imported into Keyfactor. Additionally, this tool requires a properly installed and functioning AnyGateway configured to work with Keyfactor and Digicert.
2622

2723
## Installation and Usage
28-
The tool comes as a Windows executable. The tool performs synchronization each time its run. For the tool to run automatically, it needs to be added as a scheduled process using Windows. The advised interval for running it is once per week. The files App.config and manualfields.json need to be present in the same directory as the tool for it to run correctly. The specific location from which the tool is ran does not matter, but it needs to have access to both the Keyfactor API endpoint as well as Digicert, and appropriate permissions for access to the configuration files.
24+
The tool comes as a Windows executable. The tool performs synchronization each time its run. For the tool to run automatically, it needs to be added as a scheduled process using Windows. The advised interval for running it is once per week. The files DigicertMetadataSync.dll.config and manualfields.json need to be present in the same directory as the tool for it to run correctly. The specific location from which the tool is ran does not matter, but it needs to have access to both the Keyfactor API endpoint as well as Digicert, and appropriate permissions for access to the configuration files.
2925
An explanation for the settings found in these files is given below.
3026

31-
## Command Line Arguments
32-
One of these two arguments needs to be used for the tool to run.
33-
- <b>"kftodc"</b>
34-
Syncronizes the contents of custom fields listed in manualfields.json from Keyfactor to DigiCert. If the fields in manualfields.json do not exist in Keyfactor or DigiCert, they are created first. Example: ```.\DigicertMetadataSync.exe kftodc```
35-
- <b>"dctokf"</b>
36-
Syncronizes the contents of both custom and non-custom fields from DigiCert to Keyfactor. The fields are listed in manualfields.json, and are created if necessary.
37-
Example: ```.\DigicertMetadataSync.exe dctokf```
3827

3928
## Settings
4029
The settings currently present in these files are shown as an example and need to be configured for your specific situation.
41-
### app.config settings
30+
### DigicertMetadataSync.dll.config settings
4231
- <b>DigicertAPIKey</b>
43-
Standard DigiCert API access key.
44-
- <b>DigicertAPIKeyTopPerm</b>
45-
DigiCert API access key with restrictions set to "None" - <b>required for sync from Keyfactor to DigiCert</b>.
32+
Standard DigiCert API access key
4633
- <b>KeyfactorDomainAndUser</b>
4734
Same credential as used when logging into Keyfactor Command. A different set of credentials can be used provided they have adequate access permissions.
4835
- <b>KeyfactorPassword</b>
@@ -86,4 +73,6 @@ String to be input into Keyfactor as the metadata field hint.
8673
- <b>KeyfactorAllowAPI</b>
8774
Allows API management of this metadata field in Keyfactor. Should be set to true for continuous synchronization with this tool.
8875

76+
### Logging
77+
Logging functionality can be configured via entering either "Debug" or "Trace" into the value of `<variable name="minLogLevel" value="Debug" />` in NLog.config.
8978

digicert-metadata-sync/AddFieldsToKeyfactor.cs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,19 @@ namespace DigicertMetadataSync;
2020

2121
// This fuction adds the fields to keyfactor.
2222
// It will only add new fields.
23-
partial class DigicertSync
23+
internal partial class DigicertSync
2424
{
25-
public static Tuple<int,List<string>> AddFieldsToKeyfactor(List<KeyfactorMetadataInstanceSendoff> inputlist,
25+
public static Tuple<int, List<string>> AddFieldsToKeyfactor(List<KeyfactorMetadataInstanceSendoff> inputlist,
2626
List<KeyfactorMetadataInstance> existingmetadatalist, bool noexistingfields, string keyfactorusername,
2727
string keyfactorpassword, string keyfactorapilocation)
2828
{
2929
var addfieldstokeyfactorurl = keyfactorapilocation + "MetadataFields";
3030
var addfieldsclient = new RestClient();
3131
addfieldsclient.Authenticator = new HttpBasicAuthenticator(keyfactorusername, keyfactorpassword);
32-
int totalnumberadded = 0;
33-
List<string> newfields = new List<string>();
32+
var totalnumberadded = 0;
33+
var newfields = new List<string>();
3434
if (inputlist.Count != 0)
35-
{
3635
foreach (var metadatainstance in inputlist)
37-
{
3836
if (noexistingfields == false)
3937
{
4038
var fieldquery = from existingmetadatainstance in existingmetadatalist
@@ -50,7 +48,7 @@ public static Tuple<int,List<string>> AddFieldsToKeyfactor(List<KeyfactorMetadat
5048
addfieldrequest.AddHeader("x-keyfactor-requested-with", "APIClient");
5149
var serializedfield = JsonConvert.SerializeObject(metadatainstance);
5250
addfieldrequest.AddParameter("application/json", serializedfield, ParameterType.RequestBody);
53-
RestResponse metadataresponse = new RestResponse();
51+
var metadataresponse = new RestResponse();
5452
try
5553
{
5654
metadataresponse = addfieldsclient.Post(addfieldrequest);
@@ -68,7 +66,7 @@ public static Tuple<int,List<string>> AddFieldsToKeyfactor(List<KeyfactorMetadat
6866
if (fieldquery.FirstOrDefault().DataType != metadatainstance.DataType)
6967
{
7068
//Throw error if datatype included in keyfactor does not match the digicert one.
71-
NotSupportedException mismatchedtypes = new NotSupportedException();
69+
var mismatchedtypes = new NotSupportedException();
7270
throw mismatchedtypes;
7371
}
7472
}
@@ -82,7 +80,7 @@ public static Tuple<int,List<string>> AddFieldsToKeyfactor(List<KeyfactorMetadat
8280
addfieldrequest.AddHeader("x-keyfactor-requested-with", "APIClient");
8381
var serializedfield = JsonConvert.SerializeObject(metadatainstance);
8482
addfieldrequest.AddParameter("application/json", serializedfield, ParameterType.RequestBody);
85-
RestResponse metadataresponse = new RestResponse();
83+
var metadataresponse = new RestResponse();
8684
try
8785
{
8886
metadataresponse = addfieldsclient.Post(addfieldrequest);
@@ -94,9 +92,8 @@ public static Tuple<int,List<string>> AddFieldsToKeyfactor(List<KeyfactorMetadat
9492
throw e;
9593
}
9694
}
97-
}
98-
}
99-
Tuple<int, List<string>> returnvals = new Tuple<int, List<string>>(totalnumberadded, newfields);
95+
96+
var returnvals = new Tuple<int, List<string>>(totalnumberadded, newfields);
10097

10198
return returnvals;
10299
}

digicert-metadata-sync/App.config

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
<add key="DigicertAPIKeyTopPerm" value="" />
66
<add key="KeyfactorDomainAndUser" value="" />
77
<add key="KeyfactorPassword" value="" />
8-
<add key="KeyfactorCertSearchReturnLimit" value="500" />
8+
<add key="KeyfactorCertSearchReturnLimit" value="" />
99
<add key="KeyfactorAPIEndpoint" value="" />
1010
<add key="KeyfactorDigicertIssuedCertQueryTerm" value="DigiCert" />
1111
<add key="ImportAllCustomDigicertFields" value="False" />
1212
<add key="ReplaceDigicertWhiteSpaceCharacterInName" value="_-_" />
1313
</appSettings>
14-
</configuration>
14+
</configuration>

digicert-metadata-sync/DigicertMetadataSync.csproj

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@
1010

1111
<ItemGroup>
1212
<PackageReference Include="Keyfactor.Logging" Version="1.1.0" />
13-
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" />
14-
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
15-
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
16-
<PackageReference Include="RestSharp" Version="108.0.1" />
17-
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.0" />
13+
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.4" />
14+
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
15+
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
16+
<PackageReference Include="NLog" Version="5.2.0" />
17+
<PackageReference Include="RestSharp" Version="108.0.3" />
18+
<PackageReference Include="System.Configuration.ConfigurationManager" Version="7.0.0" />
1819
<None Update="App.config">
1920
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
2021
</None>
@@ -24,6 +25,9 @@
2425
<None Update="customfields.json">
2526
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
2627
</None>
28+
<None Update="NLog.config">
29+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
30+
</None>
2731
</ItemGroup>
2832

2933
</Project>

digicert-metadata-sync/GrabCustomFieldsFromDigiCert.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ partial class DigicertSync
2626
{
2727
public static List<CustomDigicertMetadataInstance> GrabCustomFieldsFromDigiCert(string apikey)
2828
{
29-
ILogger logger = LogHandler.GetClassLogger<DigicertSync>();
3029
var digicertclient = new RestClient();
3130
var customfieldsretrieval = "https://www.digicert.com/services/v2/account/metadata";
3231
var digicertrequest = new RestRequest(customfieldsretrieval);
@@ -38,7 +37,7 @@ public static List<CustomDigicertMetadataInstance> GrabCustomFieldsFromDigiCert(
3837
trimmeddigicertresponse = trimmeddigicertresponse.Remove(lengthofresponse - 1, 1);
3938
var fieldlist = JsonConvert.DeserializeObject<List<CustomDigicertMetadataInstance>>(trimmeddigicertresponse);
4039
Console.WriteLine("Obtained custom fields from DigiCert.");
41-
logger.LogDebug("Obtained custom fields from DigiCert.");
40+
_logger.Debug("Obtained custom fields from DigiCert.");
4241
return fieldlist;
4342
}
4443
}

digicert-metadata-sync/Helpers.cs

Lines changed: 23 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -12,57 +12,46 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
using System.Reflection;
1615
using System.Text.RegularExpressions;
16+
using Newtonsoft.Json.Linq;
1717

1818
namespace DigicertMetadataSync;
1919

20-
partial class DigicertSync
20+
internal partial class DigicertSync
2121
{
2222
public static int TypeMatcher(string digicerttype)
2323
{
2424
if (digicerttype.Contains("int") || digicerttype.Contains("Int"))
25-
{
2625
// 2 matches the keyfactor int type metadata field
2726
return 2;
28-
}
29-
else
30-
{
31-
//1 matches the keyfactor string type
32-
return 1;
33-
}
27+
//1 matches the keyfactor string type
28+
return 1;
3429
}
3530
}
3631

37-
partial class DigicertSync
32+
internal partial class DigicertSync
3833
{
3934
public static Dictionary<string, object> ClassConverter(object obj)
4035
{
4136
if (obj != null && obj != "")
4237
{
43-
Dictionary<string, object> resultdict = new Dictionary<string, object>();
38+
var resultdict = new Dictionary<string, object>();
4439
var propertylist = obj.GetType().GetProperties();
4540

46-
foreach (PropertyInfo prop in propertylist)
41+
foreach (var prop in propertylist)
4742
{
48-
string propName = prop.Name;
43+
var propName = prop.Name;
4944
var val = obj.GetType().GetProperty(propName).GetValue(obj, null);
5045
if (val != null)
51-
{
5246
resultdict.Add(propName, val);
53-
}
5447
else
55-
{
5648
resultdict.Add(propName, "");
57-
}
5849
}
5950

6051
return resultdict;
6152
}
62-
else
63-
{
64-
return null;
65-
}
53+
54+
return null;
6655
}
6756

6857
public static string ReplaceAllWhiteSpaces(string str, string replacement)
@@ -72,78 +61,47 @@ public static string ReplaceAllWhiteSpaces(string str, string replacement)
7261

7362
public static bool CheckMode(string mode)
7463
{
75-
if ((mode == "kftodc") || (mode == "dctokf")){
76-
return true;
77-
}
64+
if (mode == "kftodc" || mode == "dctokf") return true;
7865
return false;
7966
}
8067

8168
private static List<KeyfactorMetadataInstanceSendoff> convertlisttokf(List<ReadInMetadataField> inputlist,
8269
string replacementcharacter)
8370
{
84-
List<KeyfactorMetadataInstanceSendoff> formattedlist = new List<KeyfactorMetadataInstanceSendoff>();
71+
var formattedlist = new List<KeyfactorMetadataInstanceSendoff>();
8572
if (inputlist.Count != 0)
86-
{
87-
foreach (ReadInMetadataField input in inputlist)
73+
foreach (var input in inputlist)
8874
{
89-
KeyfactorMetadataInstanceSendoff formatinstance = new KeyfactorMetadataInstanceSendoff();
75+
var formatinstance = new KeyfactorMetadataInstanceSendoff();
9076
if (input.KeyfactorMetadataFieldName == null || input.KeyfactorMetadataFieldName == "")
91-
{
9277
//If name is emtpy, use autocomplete.
9378
formatinstance.Name = ReplaceAllWhiteSpaces(input.DigicertFieldName, replacementcharacter);
94-
}
9579
else
96-
{
9780
//Use user input preferred name.
9881
formatinstance.Name = input.KeyfactorMetadataFieldName;
99-
}
10082

10183
formatinstance.AllowAPI = Convert.ToBoolean(input.KeyfactorAllowAPI);
10284
formatinstance.Hint = input.KeyfactorHint;
10385
formatinstance.DataType = TypeMatcher(input.KeyfactorDataType);
10486
formatinstance.Description = input.KeyfactorDescription;
10587
formattedlist.Add(formatinstance);
10688
}
107-
}
10889

10990
return formattedlist;
11091
}
11192

112-
private static Dictionary<string, object> recursiveopener(Dictionary<string, object> dictin, List<string> keys,
113-
int limit)
93+
public static JObject Flatten(JObject jObject, string parentName = "")
11494
{
115-
// Recursion steps
116-
// Each iteration: access dict at keys[0], pop back off keys. K
117-
object result = new object();
118-
if (dictin == null)
95+
var result = new JObject();
96+
foreach (var property in jObject.Properties())
11997
{
120-
return null;
121-
}
122-
123-
if (limit != 0)
124-
{
125-
var location = keys[0];
126-
keys.RemoveAt(0);
127-
--limit;
128-
if (limit == 0)
129-
{
130-
//At the bottom of keys
131-
Dictionary<string, object> newdict = new Dictionary<string, object>();
132-
newdict[location] = dictin[location];
133-
return newdict;
134-
}
98+
var propName = string.IsNullOrEmpty(parentName) ? property.Name : $"{parentName}.{property.Name}";
99+
if (property.Value is JObject nestedObject)
100+
result.Merge(Flatten(nestedObject, propName));
135101
else
136-
{
137-
//Keep recursion
138-
139-
Dictionary<string, object> newdict = ClassConverter(dictin[location]);
140-
return recursiveopener(newdict, keys, limit);
141-
}
142-
}
143-
else
144-
{
145-
//Found nothing.
146-
return null;
102+
result[propName] = property.Value;
147103
}
104+
105+
return result;
148106
}
149107
}

0 commit comments

Comments
 (0)