Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ public class RedactionBenchmark

public RedactionBenchmark()
{
_routeParameterDataClasses.Add("userId", FakeClassifications.PrivateData);
_routeParameterDataClasses.Add("chatId", FakeClassifications.PrivateData);
_routeParameterDataClasses.Add("userId", FakeTaxonomy.PrivateData);
_routeParameterDataClasses.Add("chatId", FakeTaxonomy.PrivateData);

_httpPath = "/users/{userId}/chats/{chatId}/test1/test2/{userId}";
_stringBuilderPool = PoolFactory.CreateStringBuilderPool();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public static System.Net.Http.HttpClient CreateWithLoggingLogRequest(string file
{
options.BodySizeLimit = readLimit;
options.RequestBodyContentTypes.Add(new("application/json"));
options.RequestHeadersDataClasses.Add("Content-Type", FakeClassifications.PrivateData);
options.RequestHeadersDataClasses.Add("Content-Type", FakeTaxonomy.PrivateData);
})
.AddHttpMessageHandler<NoRemoteCallHandler>()
.Services
Expand All @@ -49,7 +49,7 @@ public static System.Net.Http.HttpClient CreateWithLoggingLogResponse(string fil
{
options.BodySizeLimit = readLimit;
options.ResponseBodyContentTypes.Add(new("application/json"));
options.ResponseHeadersDataClasses.Add("Content-Type", FakeClassifications.PrivateData);
options.ResponseHeadersDataClasses.Add("Content-Type", FakeTaxonomy.PrivateData);
})
.AddHttpMessageHandler<NoRemoteCallHandler>()
.Services
Expand All @@ -73,10 +73,10 @@ public static System.Net.Http.HttpClient CreateWithLoggingLogAll(string fileName
options.BodySizeLimit = readLimit;

options.RequestBodyContentTypes.Add(new("application/json"));
options.RequestHeadersDataClasses.Add("Content-Type", FakeClassifications.PrivateData);
options.RequestHeadersDataClasses.Add("Content-Type", FakeTaxonomy.PrivateData);

options.ResponseBodyContentTypes.Add(new("application/json"));
options.ResponseHeadersDataClasses.Add("Content-Type", FakeClassifications.PrivateData);
options.ResponseHeadersDataClasses.Add("Content-Type", FakeTaxonomy.PrivateData);
})
.AddHttpMessageHandler<NoRemoteCallHandler>()
.Services
Expand All @@ -99,7 +99,7 @@ public static System.Net.Http.HttpClient CreateWithLoggingLogRequest_ChunkedEnco
{
options.BodySizeLimit = readLimit;
options.RequestBodyContentTypes.Add("application/json");
options.RequestHeadersDataClasses.Add("Content-Type", FakeClassifications.PrivateData);
options.RequestHeadersDataClasses.Add("Content-Type", FakeTaxonomy.PrivateData);
})
.AddHttpMessageHandler<NoRemoteCallNotSeekableHandler>()
.Services
Expand All @@ -122,7 +122,7 @@ public static System.Net.Http.HttpClient CreateWithLoggingLogResponse_ChunkedEnc
{
options.BodySizeLimit = readLimit;
options.ResponseBodyContentTypes.Add("application/json");
options.ResponseHeadersDataClasses.Add("Content-Type", FakeClassifications.PrivateData);
options.ResponseHeadersDataClasses.Add("Content-Type", FakeTaxonomy.PrivateData);
})
.AddHttpMessageHandler<NoRemoteCallNotSeekableHandler>()
.Services
Expand All @@ -146,10 +146,10 @@ public static System.Net.Http.HttpClient CreateWithLoggingLogAll_ChunkedEncoding
options.BodySizeLimit = readLimit;

options.RequestBodyContentTypes.Add("application/json");
options.RequestHeadersDataClasses.Add("Content-Type", FakeClassifications.PrivateData);
options.RequestHeadersDataClasses.Add("Content-Type", FakeTaxonomy.PrivateData);

options.ResponseBodyContentTypes.Add("application/json");
options.ResponseHeadersDataClasses.Add("Content-Type", FakeClassifications.PrivateData);
options.ResponseHeadersDataClasses.Add("Content-Type", FakeTaxonomy.PrivateData);
})
.AddHttpMessageHandler<NoRemoteCallNotSeekableHandler>()
.Services
Expand Down
16 changes: 9 additions & 7 deletions src/Generators/Microsoft.Gen.Logging/Emission/Emitter.Method.cs
Original file line number Diff line number Diff line change
Expand Up @@ -559,23 +559,25 @@ bool GenVariableAssignments(LoggingMethod lm, string lambdaStateName, int numRes

private string MakeClassificationValue(HashSet<string> classificationTypes)
{
if (classificationTypes.Count == 1)
{
return _classificationMap[classificationTypes.First()];
}

var sb = _sbPool.GetStringBuilder();

_ = sb.Append("new global::Microsoft.Extensions.Compliance.Classification.DataClassificationSet(");

int i = 0;

foreach (var ct in classificationTypes)
{
if (sb.Length > 0)
if (i > 0)
{
_ = sb.Append(" | ");
_ = sb.Append(" , ");
}

_ = sb.Append(_classificationMap[ct]);
i++;
}

_ = sb.Append(')');

return sb.ToString();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,67 +2,50 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Shared.Diagnostics;

namespace Microsoft.Extensions.Compliance.Classification;

/// <summary>
/// Represents a set of data classes as a part of a data taxonomy.
/// Represents a single data class which is part of a data taxonomy.
/// </summary>
public readonly struct DataClassification : IEquatable<DataClassification>
{
/// <summary>
/// Represents unclassified data.
/// </summary>
public const ulong NoneTaxonomyValue = 0UL;

/// <summary>
/// Represents the unknown classification.
/// </summary>
public const ulong UnknownTaxonomyValue = 1UL << 63;

/// <summary>
/// Gets the value to represent data with no defined classification.
/// </summary>
public static DataClassification None => new(NoneTaxonomyValue);
public static DataClassification None => new("None");

/// <summary>
/// Gets the value to represent data with an unknown classification.
/// </summary>
public static DataClassification Unknown => new(UnknownTaxonomyValue);
public static DataClassification Unknown => new("Unknown");

/// <summary>
/// Gets the name of the taxonomy that recognizes this classification.
/// </summary>
public string TaxonomyName { get; }

/// <summary>
/// Gets the bit mask representing the data classes.
/// Gets the value representing the data class within the taxonomy.
/// </summary>
public ulong Value { get; }
public string Value { get; }

/// <summary>
/// Initializes a new instance of the <see cref="DataClassification"/> struct.
/// </summary>
/// <param name="taxonomyName">The name of the taxonomy this classification belongs to.</param>
/// <param name="value">The taxonomy-specific bit vector representing the data classes.</param>
/// <exception cref="ArgumentException">Bit 63, which corresponds to <see cref="UnknownTaxonomyValue"/>, is set in the <paramref name="value"/> value.</exception>
public DataClassification(string taxonomyName, ulong value)
/// <param name="value">The taxonomy-specific value representing the data class.</param>
public DataClassification(string taxonomyName, string value)
{
TaxonomyName = Throw.IfNullOrEmpty(taxonomyName);
Value = value;

if (((value & UnknownTaxonomyValue) != 0) || (value == NoneTaxonomyValue))
{
Throw.ArgumentException(nameof(value), $"Cannot create a classification with a value of 0x{value:x}.");
}
Value = Throw.IfNullOrEmpty(value);
}

private DataClassification(ulong taxonomyValue)
private DataClassification(string value)
{
TaxonomyName = string.Empty;
Value = taxonomyValue;
Value = value;
}

/// <summary>
Expand Down Expand Up @@ -110,48 +93,9 @@ public override int GetHashCode()
return !left.Equals(right);
}

/// <summary>
/// Combines together two data classifications.
/// </summary>
/// <param name="left">The first classification to combine.</param>
/// <param name="right">The second classification to combine.</param>
/// <returns>A new classification object representing the combination of the two input classifications.</returns>
/// <exception cref="ArgumentException">The two classifications aren't part of the same taxonomy.</exception>
public static DataClassification Or(DataClassification left, DataClassification right)
{
if (string.IsNullOrEmpty(left.TaxonomyName))
{
return (left.Value == NoneTaxonomyValue) ? right : Unknown;
}
else if (string.IsNullOrEmpty(right.TaxonomyName))
{
return (right.Value == NoneTaxonomyValue) ? left : Unknown;
}

if (left.TaxonomyName != right.TaxonomyName)
{
Throw.ArgumentException(nameof(right), $"Mismatched data taxonomies: {left.TaxonomyName} and {right.TaxonomyName} cannot be combined");
}

return new(left.TaxonomyName, left.Value | right.Value);
}

/// <summary>
/// Combines together two data classifications.
/// </summary>
/// <param name="left">The first classification to combine.</param>
/// <param name="right">The second classification to combine.</param>
/// <returns>A new classification object representing the combination of the two input classifications.</returns>
/// <exception cref="ArgumentException">The two classifications aren't part of the same taxonomy.</exception>
[SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "It's called Combine")]
public static DataClassification operator |(DataClassification left, DataClassification right)
{
return Or(left, right);
}

/// <summary>
/// Gets a string representation of this object.
/// </summary>
/// <returns>A string representing the object.</returns>
public override string ToString() => $"{TaxonomyName}:{Value:x}";
public override string ToString() => $"{TaxonomyName}:{Value}";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Shared.Diagnostics;
using Microsoft.Shared.Pools;

namespace Microsoft.Extensions.Compliance.Classification;

/// <summary>
/// Represents a set of data classes.
/// </summary>
public sealed class DataClassificationSet : IEquatable<DataClassificationSet>
{
private readonly HashSet<DataClassification> _classifications = [];

/// <summary>
/// Initializes a new instance of the <see cref="DataClassificationSet"/> class.
/// </summary>
/// <param name="classification">The class to include in the set.</param>
public DataClassificationSet(DataClassification classification)
{
_ = _classifications.Add(classification);
}

/// <summary>
/// Initializes a new instance of the <see cref="DataClassificationSet"/> class.
/// </summary>
/// <param name="classifications">The classes to include in the set.</param>
public DataClassificationSet(IEnumerable<DataClassification> classifications)
{
_ = Throw.IfNull(classifications);
_classifications.UnionWith(classifications);
}

/// <summary>
/// Initializes a new instance of the <see cref="DataClassificationSet"/> class.
/// </summary>
/// <param name="classifications">The classes to include in the set.</param>
public DataClassificationSet(params DataClassification[] classifications)
{
_ = Throw.IfNull(classifications);
_classifications.UnionWith(classifications);
}

/// <summary>
/// Converts a data classification to a data classification set.
/// </summary>
/// <param name="classification">The classification to include in the set.</param>
public static implicit operator DataClassificationSet(DataClassification classification)
{
return FromDataClassification(classification);
}

/// <summary>
/// Converts a data classification to a data classification set.
/// </summary>
/// <param name="classification">The classification to include in the set.</param>
/// <returns>The resulting data classification set.</returns>
public static DataClassificationSet FromDataClassification(DataClassification classification) => new(classification);

/// <summary>
/// Creates a new data classification that combines the current classifications with another set.
/// </summary>
/// <param name="other">The other set.</param>
/// <returns>The resulting set which combines the current instance's classifications and the other ones.</returns>
public DataClassificationSet Union(DataClassificationSet other)
{
_ = Throw.IfNull(other);

var result = new DataClassificationSet(other._classifications);
result._classifications.UnionWith(_classifications);

return result;
}

/// <summary>
/// Gets a hash code for the current object instance.
/// </summary>
/// <returns>The hash code value.</returns>
public override int GetHashCode() => _classifications.GetHashCode();

/// <summary>
/// Compares an object with the current instance to see if they contain the same data classes.
/// </summary>
/// <param name="obj">The other data classification to compare to.</param>
/// <returns><see langword="true"/> if the two sets contain the same classifications.</returns>
public override bool Equals(object? obj) => Equals(obj as DataClassificationSet);

/// <summary>
/// Compares an object with the current instance to see if they contain the same data classes.
/// </summary>
/// <param name="other">The other data classification to compare to.</param>
/// <returns><see langword="true"/> if the two sets contain the same classifications.</returns>
public bool Equals(DataClassificationSet? other)
{
if (other == null)
{
return false;
}

return _classifications.SetEquals(other._classifications);
}

/// <summary>
/// Returns a string representation of this object.
/// </summary>
/// <returns>The string representation of this object.</returns>
public override string ToString()
{
var sb = PoolFactory.SharedStringBuilderPool.Get();

var a = _classifications.ToArray();
Array.Sort(a, (x, y) =>
{
var result = string.Compare(x.TaxonomyName, y.TaxonomyName, StringComparison.Ordinal);
if (result == 0)
{
result = string.Compare(x.Value, y.Value, StringComparison.Ordinal);
}

return result;
});

_ = sb.Append('[');
foreach (var c in a)
{
if (sb.Length > 1)
{
_ = sb.Append(',');
}

_ = sb.Append(c);
}

_ = sb.Append(']');
var result = sb.ToString();
PoolFactory.SharedStringBuilderPool.Return(sb);

return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@

<PropertyGroup>
<InjectSkipLocalsInitAttributeOnLegacy>true</InjectSkipLocalsInitAttributeOnLegacy>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<InjectSharedPools>true</InjectSharedPools>
<InjectSharedRentedSpan>true</InjectSharedRentedSpan>
<InjectSharedDiagnosticIds>true</InjectSharedDiagnosticIds>
<InjectExperimentalAttributeOnLegacy>true</InjectExperimentalAttributeOnLegacy>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<PropertyGroup>
Expand Down
Loading