private void AddNoise(ElementNode node, PerturbSetting perturbSetting) { if (s_integerValueTypeNames.Contains(node.InstanceType, StringComparer.InvariantCultureIgnoreCase)) { perturbSetting.RoundTo = 0; } var originValue = decimal.Parse(node.Value.ToString()); var span = perturbSetting.Span; if (perturbSetting.RangeType == PerturbRangeType.Proportional) { span = (double)originValue * perturbSetting.Span; } var noise = (decimal)ContinuousUniform.Sample(-1 * span / 2, span / 2); var perturbedValue = decimal.Round(originValue + noise, perturbSetting.RoundTo); if (perturbedValue <= 0 && string.Equals(FHIRAllTypes.PositiveInt.ToString(), node.InstanceType, StringComparison.InvariantCultureIgnoreCase)) { perturbedValue = 1; } if (perturbedValue < 0 && string.Equals(FHIRAllTypes.UnsignedInt.ToString(), node.InstanceType, StringComparison.InvariantCultureIgnoreCase)) { perturbedValue = 0; } node.Value = perturbedValue; return; }
public PerturbFunction(PerturbSetting perturbSetting) { EnsureArg.IsNotNull(perturbSetting, nameof(perturbSetting)); _perturbSetting = perturbSetting ?? new PerturbSetting(); _perturbSetting.Validate(); }
public ProcessResult Process(ElementNode node, ProcessContext context = null, Dictionary <string, object> settings = null) { EnsureArg.IsNotNull(node); EnsureArg.IsNotNull(context?.VisitedNodes); EnsureArg.IsNotNull(settings); var result = new ProcessResult(); ElementNode valueNode = null; if (s_primitiveValueTypeNames.Contains(node.InstanceType, StringComparer.InvariantCultureIgnoreCase)) { valueNode = node; } else if (s_quantityTypeNames.Contains(node.InstanceType, StringComparer.InvariantCultureIgnoreCase)) { valueNode = node.Children(Constants.ValueNodeName).Cast <ElementNode>().FirstOrDefault(); } // Perturb will not happen if value node is empty or visited. if (valueNode?.Value == null || context.VisitedNodes.Contains(valueNode)) { return(result); } var perturbSetting = PerturbSetting.CreateFromRuleSettings(settings); AddNoise(valueNode, perturbSetting); context.VisitedNodes.UnionWith(node.Descendants().Cast <ElementNode>()); result.AddProcessRecord(AnonymizationOperations.Perturb, node); return(result); }
public void GivenAPerturbSetting_WhenCreate_SettingPropertiesShouldBeParsedCorrectly(Dictionary <string, object> config, double expectedSpan, double expectedRoundTo, PerturbRangeType expectedRangeType) { var perturbSetting = PerturbSetting.CreateFromRuleSettings(config); Assert.Equal(expectedSpan, perturbSetting.Span); Assert.Equal(expectedRoundTo, perturbSetting.RoundTo); Assert.Equal(expectedRangeType, perturbSetting.RangeType); }
public void Validate(AnonymizerConfiguration config) { if (string.IsNullOrEmpty(config.FhirVersion)) { _logger.LogWarning($"Version is not specified in configuration file."); } else if (!string.Equals(Constants.SupportedVersion, config.FhirVersion, StringComparison.InvariantCultureIgnoreCase)) { throw new AnonymizerConfigurationErrorsException($"Configuration of fhirVersion {config.FhirVersion} is not supported. Expected fhirVersion: {Constants.SupportedVersion}"); } if (config.FhirPathRules == null) { throw new AnonymizerConfigurationErrorsException("The configuration is invalid, please specify any fhirPathRules"); } FhirPathCompiler compiler = new FhirPathCompiler(); var supportedMethods = Enum.GetNames(typeof(AnonymizerMethod)).ToHashSet(StringComparer.InvariantCultureIgnoreCase); foreach (var rule in config.FhirPathRules) { if (!rule.ContainsKey(Constants.PathKey) || !rule.ContainsKey(Constants.MethodKey)) { throw new AnonymizerConfigurationErrorsException("Missing path or method in Fhir path rule config."); } // Grammar check on FHIR path try { compiler.Compile(rule[Constants.PathKey].ToString()); } catch (Exception ex) { throw new AnonymizerConfigurationErrorsException($"Invalid FHIR path {rule[Constants.PathKey]}", ex); } // Method validate string method = rule[Constants.MethodKey].ToString(); if (!supportedMethods.Contains(method)) { throw new AnonymizerConfigurationErrorsException($"Anonymization method {method} not supported."); } // Should provide replacement value for substitute rule if (string.Equals(method, AnonymizerMethod.Substitute.ToString(), StringComparison.InvariantCultureIgnoreCase)) { SubstituteSetting.ValidateRuleSettings(rule); } if (string.Equals(method, AnonymizerMethod.Perturb.ToString(), StringComparison.InvariantCultureIgnoreCase)) { PerturbSetting.ValidateRuleSettings(rule); } if (string.Equals(method, AnonymizerMethod.Generalize.ToString(), StringComparison.InvariantCultureIgnoreCase)) { GeneralizeSetting.ValidateRuleSettings(rule); } } // Check AES key size is valid (16, 24 or 32 bytes). if (!string.IsNullOrEmpty(config.ParameterConfiguration?.EncryptKey)) { using Aes aes = Aes.Create(); var encryptKeySize = Encoding.UTF8.GetByteCount(config.ParameterConfiguration.EncryptKey) * 8; if (!IsValidKeySize(encryptKeySize, aes.LegalKeySizes)) { throw new AnonymizerConfigurationErrorsException($"Invalid encrypt key size : {encryptKeySize} bits! Please provide key sizes of 128, 192 or 256 bits."); } } }
public void GivenAInvalidPerturbSetting_WhenValidate_ExceptionShouldBeThrown(Dictionary <string, object> config) { Assert.Throws <AnonymizerConfigurationException>(() => PerturbSetting.ValidateRuleSettings(config)); }
public void GivenAnUnsignedLongInteger_WhenPerturb_PerturbedValueShouldBeReturned(ulong value, PerturbSetting perturbSetting, ulong lowerBound, ulong upperBound) { var function = new PerturbFunction(perturbSetting); var result = function.Perturb(value); Assert.InRange(result, lowerBound, upperBound); Assert.IsType <ulong>(result); }
public void GivenALongInteger_WhenPerturb_PerturbedValueShouldBeReturned(long value, PerturbSetting perturbSetting, long lowerBound, long upperBound) { var function = new PerturbFunction(perturbSetting); var result = function.Perturb(value); Assert.InRange(result, lowerBound, upperBound); Assert.True(GetDecimalPlaces(result) <= perturbSetting.RoundTo); Assert.IsType <long>(result); }
public void GivenAnUnsignedShortInteger_WhenPerturb_PerturbedValueShouldBeReturned(ushort value, PerturbSetting perturbSetting, ushort lowerBound, ushort upperBound) { var function = new PerturbFunction(perturbSetting); var result = function.Perturb(value); Assert.InRange(result, lowerBound, upperBound); Assert.True(GetDecimalPlaces(result) <= perturbSetting.RoundTo); Assert.IsType <ushort>(result); }
public void GivenAFloat_WhenPerturb_PerturbedValueShouldBeReturned(float value, PerturbSetting perturbSetting, float lowerBound, float upperBound) { var function = new PerturbFunction(perturbSetting); var result = function.Perturb(value); Assert.InRange(result, lowerBound, upperBound); Assert.True(GetDecimalPlaces((decimal)result) <= perturbSetting.RoundTo); Assert.IsType <float>(result); }
public void GivenAgeValue_WhenPerturb_PerturbedValueShouldBeReturned(AgeObject value, PerturbSetting perturbSetting, uint lowerBound, uint upperBound) { var function = new PerturbFunction(perturbSetting); var result = function.Perturb(value); Assert.InRange(result.Value, lowerBound, upperBound); Assert.Equal(value.AgeType, value.AgeType); }
public void GivenAValueString_WhenPerturb_PerturbedValueShouldBeReturned(string value, PerturbSetting perturbSetting, decimal lowerBound, decimal upperBound) { var function = new PerturbFunction(perturbSetting); var result = function.Perturb(value); Assert.IsType <string>(result); Assert.InRange(decimal.Parse(result), lowerBound, upperBound); Assert.True(GetDecimalPlaces(decimal.Parse(result)) <= perturbSetting.RoundTo); }