Skip to content

Commit

Permalink
refactor: hide conditional reference pseudonymization behind a featur…
Browse files Browse the repository at this point in the history
…e flag
  • Loading branch information
chgl committed Jan 19, 2023
1 parent 9bd3806 commit f3a08d7
Show file tree
Hide file tree
Showing 7 changed files with 45 additions and 18 deletions.
20 changes: 14 additions & 6 deletions src/FhirPseudonymizer.Tests/GPasPseudonymizationProcessorTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using FakeItEasy;
using FhirPseudonymizer.Config;
using FhirPseudonymizer.Pseudonymization;
using Hl7.Fhir.ElementModel;
using Hl7.Fhir.FhirPath;
Expand All @@ -14,19 +15,26 @@ public class GPasPseudonymizationProcessorTests
{
public static IEnumerable<object[]> GetProcessData()
{
yield return new object[] { "foo-", "bar", new FhirString("12345"), "foo-bar" };
yield return new object[] { null, "bar", new FhirString("12345"), "bar" };
yield return new object[] { "foo-", null, new ResourceReference("Patient/12345"), "foo-Patient" };
yield return new object[] { null, null, new ResourceReference("Patient/12345"), "Patient" };
foreach (var enableConditionalReferencePseudonymization in new[] { true, false })
{
yield return new object[] { "foo-", "bar", new FhirString("12345"), "foo-bar", enableConditionalReferencePseudonymization };
yield return new object[] { null, "bar", new FhirString("12345"), "bar", enableConditionalReferencePseudonymization };
yield return new object[] { "foo-", null, new ResourceReference("Patient/12345"), "foo-Patient", enableConditionalReferencePseudonymization };
yield return new object[] { null, null, new ResourceReference("Patient/12345"), "Patient", enableConditionalReferencePseudonymization };
}
}

[Theory]
[MemberData(nameof(GetProcessData))]
public void Process_SupportsDomainPrefixSetting(string domainPrefix, string domainName, DataType element,
string expectedDomain)
string expectedDomain, bool enableConditionalReferencePseudonymization)
{
var features = new FeatureManagement()
{
ConditionalReferencePseudonymization = enableConditionalReferencePseudonymization,
};
var psnClient = A.Fake<IPseudonymServiceClient>();
var processor = new PseudonymizationProcessor(psnClient);
var processor = new PseudonymizationProcessor(psnClient, features);

var node = ElementNode.FromElement(element.ToTypedElement());
while (!node.HasValue())
Expand Down
2 changes: 0 additions & 2 deletions src/FhirPseudonymizer.Tests/ReferenceUtilityTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ namespace Microsoft.Health.Fhir.Anonymizer.Core.Utility;

public class ReferenceUtilityTests
{

[Theory]
[InlineData("Patient/123")]
[InlineData("Encounter?identifier=123")]
Expand All @@ -23,5 +22,4 @@ public void TransformReferenceId_MatchesConditionalReferences(string uri)
{
Assert.Equal(ReferenceUtility.TransformReferenceId(uri, _ => "xxx"), uri.Replace("123", "xxx"));
}

}
4 changes: 2 additions & 2 deletions src/FhirPseudonymizer/AnonymizerEngineExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public static IServiceCollection AddAnonymizerEngine(this IServiceCollection ser
var engine = new AnonymizerEngine(anonConfig);

var psnClient = sp.GetRequiredService<IPseudonymServiceClient>();
engine.AddProcessor("pseudonymize", new PseudonymizationProcessor(psnClient));
engine.AddProcessor("pseudonymize", new PseudonymizationProcessor(psnClient, appConfig.Features));

return engine;
});
Expand All @@ -34,7 +34,7 @@ public static IServiceCollection AddAnonymizerEngine(this IServiceCollection ser
var engine = new DePseudonymizerEngine(anonConfig);

var psnClient = sp.GetRequiredService<IPseudonymServiceClient>();
engine.AddProcessor("pseudonymize", new DePseudonymizationProcessor(psnClient));
engine.AddProcessor("pseudonymize", new DePseudonymizationProcessor(psnClient, appConfig.Features));

engine.AddProcessor("encrypt", new DecryptProcessor(anonConfig.GetParameterConfiguration().EncryptKey));
return engine;
Expand Down
6 changes: 6 additions & 0 deletions src/FhirPseudonymizer/Config/AppConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public record AppConfig
public GPasConfig GPas { get; init; } = new();
public VfpsConfig Vfps { get; init; } = new();
public ushort MetricsPort { get; set; } = 8081;
public FeatureManagement Features { get; set; } = new();
}

public record CacheConfig
Expand Down Expand Up @@ -49,3 +50,8 @@ public record PseudonymServiceBasicAuthConfig
public string Username { get; init; }
public string Password { get; init; }
}

public record FeatureManagement
{
public bool ConditionalReferencePseudonymization { get; init; }
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using FhirPseudonymizer.Config;
using Hl7.Fhir.ElementModel;
using Microsoft.Health.Fhir.Anonymizer.Core.Extensions;
using Microsoft.Health.Fhir.Anonymizer.Core.Models;
Expand All @@ -9,15 +10,20 @@

namespace FhirPseudonymizer.Pseudonymization
{
public class PseudonymizationProcessor : IAnonymizerProcessor
public partial class PseudonymizationProcessor : IAnonymizerProcessor
{
public PseudonymizationProcessor(IPseudonymServiceClient psnClient)
public PseudonymizationProcessor(IPseudonymServiceClient psnClient, FeatureManagement features)
{
PsnClient = psnClient;
IsConditionalReferencePseudonymizationEnabled = features.ConditionalReferencePseudonymization;
}

[GeneratedRegex("^(?<domain>.*?)(\\/|\\?)")]
private static partial Regex ResourceTypeRegex();

protected IPseudonymServiceClient PsnClient { get; }
private Regex ResourceTypeMatcher { get; } = new(@"^(?<domain>.*?)(\/|\?)");
private Regex ResourceTypeMatcher { get; } = ResourceTypeRegex();
private bool IsConditionalReferencePseudonymizationEnabled { get; }

public ProcessResult Process(
ElementNode node,
Expand All @@ -44,19 +50,24 @@ public ProcessResult Process(

// Pseudonymize the id part for "Reference.reference" node and
// pseudonymize whole input for other node types
if (node.IsReferenceStringNode() || IsReferenceUriNode(node, input) || IsConditionalElement(node, input))
if (node.IsReferenceStringNode() || IsReferenceUriNode(node, input))
{
// if the domain setting is not set,
// create a domain from the reference, ie "Patient/123" -> "Patient"
domain ??= ResourceTypeMatcher
.Match(ReferenceUtility.GetReferencePrefix(input))
.Groups["domain"].Value;
domain ??= ReferenceUtility
.GetReferencePrefix(input)
.TrimEnd('/');

node.Value = ReferenceUtility.TransformReferenceId(
input,
x => GetOrCreatePseudonym(x, domainPrefix.ToString() + domain)
);
}
else if (IsConditionalReferencePseudonymizationEnabled && IsConditionalElement(node, input))
{
domain ??= ResourceTypeMatcher.Match(ReferenceUtility
.GetReferencePrefix(input)).Groups["domain"].Value;
}
else
{
node.Value = GetOrCreatePseudonym(input, domainPrefix.ToString() + domain);
Expand Down Expand Up @@ -86,7 +97,7 @@ private static bool IsConditionalElement(ElementNode node, string value)

public class DePseudonymizationProcessor : PseudonymizationProcessor
{
public DePseudonymizationProcessor(IPseudonymServiceClient psnClient) : base(psnClient) { }
public DePseudonymizationProcessor(IPseudonymServiceClient psnClient, FeatureManagement features) : base(psnClient, features) { }

protected override string GetOrCreatePseudonym(string input, string domain)
{
Expand Down
1 change: 1 addition & 0 deletions src/FhirPseudonymizer/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public void ConfigureServices(IServiceCollection services)

services.AddSingleton(_ => appConfig);
services.AddSingleton(_ => appConfig.GPas);
services.AddSingleton(_ => appConfig.Features);

services.AddApiKeyAuth(appConfig.ApiKey);

Expand Down
3 changes: 3 additions & 0 deletions src/FhirPseudonymizer/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,8 @@
"Address": "",
"UnsafeUseInsecureChannelCallCredentials": true,
"UseTls": false
},
"Features ": {
"ConditionalReferencePseudonymization": false
}
}

0 comments on commit f3a08d7

Please sign in to comment.