.NET: Implement Purview middleware in dotnet (#1949)

* Move Purview integration logic into middleware

* Improve error handling and user id management

* Rename purview package

* Handle 402s more explicitly; add Middleware generation methods; don't ignore exceptions

* Use DI container; pass scope id to PC

* Add protection scope caching

* Wrap more exceptions in PurviewClient

* Remove block check dedup; add tests

* Refactor PurviewWrapper intialization; Add unit tests

* Use different .Use method and add IDisposable stub

* Add background job processing for Purview

* Misc comment cleanup

* Apply copilot comments

* Fix formatting

* Formatting other files to fix pipeline

* Small updates to settings and exceptions

* Add README

* Move Purview sample

* Address review comments and update XML comments

* Newline after namespace

* Move public Purview classes to single namespace; Clean up csproj and slnx

* Commit the renames

* Remove unused openAI dependency

---------

Co-authored-by: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com>
This commit is contained in:
eoindoherty1
2025-11-14 00:50:08 +00:00
committed by GitHub
co-authored by Dmytro Struk
parent a75590eb9b
commit b19860b8a8
80 changed files with 5727 additions and 0 deletions
@@ -0,0 +1,29 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Info about an AI agent associated with the content.
/// </summary>
internal sealed class AIAgentInfo
{
/// <summary>
/// Gets or sets agent id.
/// </summary>
[JsonPropertyName("identifier")]
public string? Identifier { get; set; }
/// <summary>
/// Gets or sets agent name.
/// </summary>
[JsonPropertyName("name")]
public string? Name { get; set; }
/// <summary>
/// Gets or sets agent version.
/// </summary>
[JsonPropertyName("version")]
public string? Version { get; set; }
}
@@ -0,0 +1,29 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Represents a plugin used in an AI interaction within the Purview SDK.
/// </summary>
internal sealed class AIInteractionPlugin
{
/// <summary>
/// Gets or sets Plugin id.
/// </summary>
[JsonPropertyName("identifier")]
public string? Identifier { get; set; }
/// <summary>
/// Gets or sets Plugin Name.
/// </summary>
[JsonPropertyName("name")]
public string? Name { get; set; }
/// <summary>
/// Gets or sets Plugin Version.
/// </summary>
[JsonPropertyName("version")]
public string? Version { get; set; }
}
@@ -0,0 +1,53 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Information about a resource accessed during a conversation.
/// </summary>
internal sealed class AccessedResourceDetails
{
/// <summary>
/// Resource ID.
/// </summary>
[JsonPropertyName("identifier")]
public string? Identifier { get; set; }
/// <summary>
/// Resource name.
/// </summary>
[JsonPropertyName("name")]
public string? Name { get; set; }
/// <summary>
/// Resource URL.
/// </summary>
[JsonPropertyName("url")]
public string? Url { get; set; }
/// <summary>
/// Sensitivity label id detected on the resource.
/// </summary>
[JsonPropertyName("labelId")]
public string? LabelId { get; set; }
/// <summary>
/// Access type performed on the resource.
/// </summary>
[JsonPropertyName("accessType")]
public ResourceAccessType AccessType { get; set; }
/// <summary>
/// Status of the access operation.
/// </summary>
[JsonPropertyName("status")]
public ResourceAccessStatus Status { get; set; }
/// <summary>
/// Indicates if cross prompt injection was detected.
/// </summary>
[JsonPropertyName("isCrossPromptInjectionDetected")]
public bool? IsCrossPromptInjectionDetected { get; set; }
}
@@ -0,0 +1,44 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Activity definitions
/// </summary>
[DataContract]
[JsonConverter(typeof(JsonStringEnumConverter<Activity>))]
internal enum Activity : int
{
/// <summary>
/// Unknown activity
/// </summary>
[EnumMember(Value = "unknown")]
Unknown = 0,
/// <summary>
/// Upload text
/// </summary>
[EnumMember(Value = "uploadText")]
UploadText = 1,
/// <summary>
/// Upload file
/// </summary>
[EnumMember(Value = "uploadFile")]
UploadFile = 2,
/// <summary>
/// Download text
/// </summary>
[EnumMember(Value = "downloadText")]
DownloadText = 3,
/// <summary>
/// Download file
/// </summary>
[EnumMember(Value = "downloadFile")]
DownloadFile = 4,
}
@@ -0,0 +1,30 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Request for metadata information
/// </summary>
[DataContract]
internal sealed class ActivityMetadata
{
/// <summary>
/// Initializes a new instance of the <see cref="ActivityMetadata"/> class.
/// </summary>
/// <param name="activity">The activity performed with the content.</param>
public ActivityMetadata(Activity activity)
{
this.Activity = activity;
}
/// <summary>
/// The activity performed with the content.
/// </summary>
[DataMember]
[JsonConverter(typeof(JsonStringEnumConverter<Activity>))]
[JsonPropertyName("activity")]
public Activity Activity { get; }
}
@@ -0,0 +1,37 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Base error contract returned when some exception occurs.
/// </summary>
[JsonDerivedType(typeof(ProcessingError))]
internal class ClassificationErrorBase
{
/// <summary>
/// Gets or sets the error code.
/// </summary>
[JsonPropertyName("code")]
public string? ErrorCode { get; set; }
/// <summary>
/// Gets or sets the message.
/// </summary>
[JsonPropertyName("message")]
public string? Message { get; set; }
/// <summary>
/// Gets or sets target of error.
/// </summary>
[JsonPropertyName("target")]
public string? Target { get; set; }
/// <summary>
/// Gets or sets an object containing more specific information than the current object about the error.
/// It can't be a Dictionary because OData will make ClassificationErrorBase open type. It's not expected behavior.
/// </summary>
[JsonPropertyName("innerError")]
public ClassificationInnerError? InnerError { get; set; }
}
@@ -0,0 +1,36 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Inner classification error.
/// </summary>
internal sealed class ClassificationInnerError
{
/// <summary>
/// Gets or sets date of error.
/// </summary>
[JsonPropertyName("date")]
public DateTime? Date { get; set; }
/// <summary>
/// Gets or sets error code.
/// </summary>
[JsonPropertyName("code")]
public string? ErrorCode { get; set; }
/// <summary>
/// Gets or sets client request ID.
/// </summary>
[JsonPropertyName("clientRequestId")]
public string? ClientRequestId { get; set; }
/// <summary>
/// Gets or sets Activity ID.
/// </summary>
[JsonPropertyName("activityId")]
public string? ActivityId { get; set; }
}
@@ -0,0 +1,21 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Base class for content items to be processed by the Purview SDK.
/// </summary>
[JsonDerivedType(typeof(PurviewTextContent))]
[JsonDerivedType(typeof(PurviewBinaryContent))]
internal abstract class ContentBase : GraphDataTypeBase
{
/// <summary>
/// Creates a new instance of the <see cref="ContentBase"/> class.
/// </summary>
/// <param name="dataType">The graph data type of the content.</param>
public ContentBase(string dataType) : base(dataType)
{
}
}
@@ -0,0 +1,27 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Type of error that occurred during content processing.
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter<ContentProcessingErrorType>))]
internal enum ContentProcessingErrorType
{
/// <summary>
/// Error is transient.
/// </summary>
Transient,
/// <summary>
/// Error is permanent.
/// </summary>
Permanent,
/// <summary>
/// Unknown future value placeholder.
/// </summary>
UnknownFutureValue
}
@@ -0,0 +1,70 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Content to be processed by process content.
/// </summary>
internal sealed class ContentToProcess
{
/// <summary>
/// Creates a new instance of ContentToProcess.
/// </summary>
/// <param name="contentEntries">The content to send and its associated ids.</param>
/// <param name="activityMetadata">Metadata about the activity performed with the content.</param>
/// <param name="deviceMetadata">Metadata about the device that produced the content.</param>
/// <param name="integratedAppMetadata">Metadata about the application integrating with Purview.</param>
/// <param name="protectedAppMetadata">Metadata about the application being protected by Purview.</param>
public ContentToProcess(
List<ProcessContentMetadataBase> contentEntries,
ActivityMetadata activityMetadata,
DeviceMetadata deviceMetadata,
IntegratedAppMetadata integratedAppMetadata,
ProtectedAppMetadata protectedAppMetadata)
{
this.ContentEntries = contentEntries;
this.ActivityMetadata = activityMetadata;
this.DeviceMetadata = deviceMetadata;
this.IntegratedAppMetadata = integratedAppMetadata;
this.ProtectedAppMetadata = protectedAppMetadata;
}
/// <summary>
/// Gets or sets the content entries.
/// List of activities supported by caller. It is used to trim response to activities interesting to the caller.
/// </summary>
[JsonPropertyName("contentEntries")]
public List<ProcessContentMetadataBase> ContentEntries { get; set; }
/// <summary>
/// Activity metadata
/// </summary>
[DataMember]
[JsonPropertyName("activityMetadata")]
public ActivityMetadata ActivityMetadata { get; set; }
/// <summary>
/// Device metadata
/// </summary>
[DataMember]
[JsonPropertyName("deviceMetadata")]
public DeviceMetadata DeviceMetadata { get; set; }
/// <summary>
/// Integrated app metadata
/// </summary>
[DataMember]
[JsonPropertyName("integratedAppMetadata")]
public IntegratedAppMetadata IntegratedAppMetadata { get; set; }
/// <summary>
/// Protected app metadata
/// </summary>
[DataMember]
[JsonPropertyName("protectedAppMetadata")]
public ProtectedAppMetadata ProtectedAppMetadata { get; set; }
}
@@ -0,0 +1,29 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Endpoint device Metdata
/// </summary>
internal sealed class DeviceMetadata
{
/// <summary>
/// Device type
/// </summary>
[JsonPropertyName("deviceType")]
public string? DeviceType { get; set; }
/// <summary>
/// The ip address of the device.
/// </summary>
[JsonPropertyName("ipAddress")]
public string? IpAddress { get; set; }
/// <summary>
/// OS specifications
/// </summary>
[JsonPropertyName("operatingSystemSpecifications")]
public OperatingSystemSpecifications? OperatingSystemSpecifications { get; set; }
}
@@ -0,0 +1,69 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Defines all the actions for DLP.
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter<DlpAction>))]
internal enum DlpAction
{
/// <summary>
/// The DLP action to notify user.
/// </summary>
NotifyUser,
/// <summary>
/// The DLP action is block.
/// </summary>
BlockAccess,
/// <summary>
/// The DLP action to apply restrictions on device.
/// </summary>
DeviceRestriction,
/// <summary>
/// The DLP action to apply restrictions on browsers.
/// </summary>
BrowserRestriction,
/// <summary>
/// The DLP action to generate an alert
/// </summary>
GenerateAlert,
/// <summary>
/// The DLP action to generate an incident report
/// </summary>
GenerateIncidentReportAction,
/// <summary>
/// The DLP action to block anonymous link access in SPO
/// </summary>
SPBlockAnonymousAccess,
/// <summary>
/// DLP Action to disallow guest access in SPO
/// </summary>
SPRuntimeAccessControl,
/// <summary>
/// DLP No Op action for NotifyUser. Used in Block Access V2 rule
/// </summary>
SPSharingNotifyUser,
/// <summary>
/// DLP No Op action for GIR. Used in Block Access V2 rule
/// </summary>
SPSharingGenerateIncidentReport,
/// <summary>
/// Restrict access action for data in motion scenarios.
/// Advanced version of BlockAccess which can take both enforced restriction mode (Audit, Block, etc.)
/// and action triggers (Print, SaveToLocal, etc.) as parameters.
/// </summary>
RestrictAccess,
}
@@ -0,0 +1,23 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Base class to define DLP Actions.
/// </summary>
internal sealed class DlpActionInfo
{
/// <summary>
/// Gets or sets the type of the DLP action.
/// </summary>
[JsonPropertyName("action")]
public DlpAction Action { get; set; }
/// <summary>
/// The type of restriction action to take.
/// </summary>
[JsonPropertyName("restrictionAction")]
public RestrictionAction? RestrictionAction { get; set; }
}
@@ -0,0 +1,23 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Represents the details of an error.
/// </summary>
internal sealed class ErrorDetails
{
/// <summary>
/// Gets or sets the error code.
/// </summary>
[JsonPropertyName("code")]
public string? Code { get; set; }
/// <summary>
/// Gets or sets the error message.
/// </summary>
[JsonPropertyName("message")]
public string? Message { get; set; }
}
@@ -0,0 +1,27 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Request execution mode
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter<ExecutionMode>))]
internal enum ExecutionMode : int
{
/// <summary>
/// Evaluate inline.
/// </summary>
EvaluateInline = 1,
/// <summary>
/// Evaluate offline.
/// </summary>
EvaluateOffline = 2,
/// <summary>
/// Unknown future value.
/// </summary>
UnknownFutureValue = 3
}
@@ -0,0 +1,26 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Base class for all graph data types used in the Purview SDK.
/// </summary>
internal abstract class GraphDataTypeBase
{
/// <summary>
/// Create a new instance of the <see cref="GraphDataTypeBase"/> class.
/// </summary>
/// <param name="dataType">The data type of the graph object.</param>
public GraphDataTypeBase(string dataType)
{
this.DataType = dataType;
}
/// <summary>
/// The @odata.type property name used in the JSON representation of the object.
/// </summary>
[JsonPropertyName(Constants.ODataTypePropertyName)]
public string DataType { get; set; }
}
@@ -0,0 +1,27 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Request for metadata information
/// </summary>
[JsonDerivedType(typeof(ProtectedAppMetadata))]
internal class IntegratedAppMetadata
{
/// <summary>
/// Application name
/// </summary>
[DataMember]
[JsonPropertyName("name")]
public string? Name { get; set; }
/// <summary>
/// Application version
/// </summary>
[DataMember]
[JsonPropertyName("version")]
public string? Version { get; set; }
}
@@ -0,0 +1,23 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Operating System Specifications
/// </summary>
internal sealed class OperatingSystemSpecifications
{
/// <summary>
/// OS platform
/// </summary>
[JsonPropertyName("operatingSystemPlatform")]
public string? OperatingSystemPlatform { get; set; }
/// <summary>
/// OS version
/// </summary>
[JsonPropertyName("operatingSystemVersion")]
public string? OperatingSystemVersion { get; set; }
}
@@ -0,0 +1,25 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Represents user scoping information, i.e. which users are affected by the policy.
/// </summary>
internal sealed class PolicyBinding
{
/// <summary>
/// Gets or sets the users to be included.
/// </summary>
[JsonPropertyName("inclusions")]
public ICollection<Scope>? Inclusions { get; set; }
/// <summary>
/// Gets or sets the users to be excluded.
/// Exclusions may not be present in the response, thus this property is nullable.
/// </summary>
[JsonPropertyName("exclusions")]
public ICollection<Scope>? Exclusions { get; set; }
}
@@ -0,0 +1,27 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Represents a location to which policy is applicable.
/// </summary>
internal sealed class PolicyLocation : GraphDataTypeBase
{
/// <summary>
/// Creates a new instance of the <see cref="PolicyLocation"/> class.
/// </summary>
/// <param name="dataType">The graph data type of the PolicyLocation object.</param>
/// <param name="value">THe value of the policy location: app id, domain, etc.</param>
public PolicyLocation(string dataType, string value) : base(dataType)
{
this.Value = value;
}
/// <summary>
/// Gets or sets the applicable value for location.
/// </summary>
[JsonPropertyName("value")]
public string Value { get; set; }
}
@@ -0,0 +1,42 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Property for policy scoping response to aggregate on
/// </summary>
[DataContract]
[JsonConverter(typeof(JsonStringEnumConverter<PolicyPivotProperty>))]
internal enum PolicyPivotProperty : int
{
/// <summary>
/// Unknown activity
/// </summary>
[EnumMember]
[JsonPropertyName("none")]
None = 0,
/// <summary>
/// Pivot on Activity
/// </summary>
[EnumMember]
[JsonPropertyName("activity")]
Activity = 1,
/// <summary>
/// Pivot on location
/// </summary>
[EnumMember]
[JsonPropertyName("location")]
Location = 2,
/// <summary>
/// Pivot on location
/// </summary>
[EnumMember]
[JsonPropertyName("unknownFutureValue")]
UnknownFutureValue = 3,
}
@@ -0,0 +1,43 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Represents a scope for policy protection.
/// </summary>
internal sealed class PolicyScopeBase
{
/// <summary>
/// Gets or sets the locations to be protected, e.g. domains or URLs.
/// </summary>
[JsonPropertyName("locations")]
public ICollection<PolicyLocation>? Locations { get; set; }
/// <summary>
/// Gets or sets the activities to be protected, e.g. uploadText, downloadText.
/// </summary>
[JsonPropertyName("activities")]
public ProtectionScopeActivities Activities { get; set; }
/// <summary>
/// Gets or sets how policy should be executed - fire-and-forget or wait for completion.
/// </summary>
[JsonPropertyName("executionMode")]
public ExecutionMode ExecutionMode { get; set; }
/// <summary>
/// Gets or sets the enforcement actions to be taken on activities and locations from this scope.
/// There may be no actions in the response.
/// </summary>
[JsonPropertyName("policyActions")]
public ICollection<DlpActionInfo>? PolicyActions { get; set; }
/// <summary>
/// Gets or sets information about policy applicability to a specific user.
/// </summary>
[JsonPropertyName("policyScope")]
public PolicyBinding? PolicyScope { get; set; }
}
@@ -0,0 +1,94 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Base class for process content metadata.
/// </summary>
[JsonDerivedType(typeof(ProcessConversationMetadata))]
[JsonDerivedType(typeof(ProcessFileMetadata))]
internal abstract class ProcessContentMetadataBase : GraphDataTypeBase
{
private const string ProcessConversationMetadataDataType = Constants.ODataGraphNamespace + ".processConversationMetadata";
/// <summary>
/// Creates a new instance of ProcessContentMetadataBase.
/// </summary>
/// <param name="content">The content that will be processed.</param>
/// <param name="identifier">The unique identifier for the content.</param>
/// <param name="isTruncated">Indicates if the content is truncated.</param>
/// <param name="name">The name of the content.</param>
public ProcessContentMetadataBase(ContentBase content, string identifier, bool isTruncated, string name) : base(ProcessConversationMetadataDataType)
{
this.Identifier = identifier;
this.IsTruncated = isTruncated;
this.Content = content;
this.Name = name;
}
/// <summary>
/// Gets or sets the identifier.
/// Unique id for the content. It is specific to the enforcement plane. Path is used as item unique identifier, e.g., guid of a message in the conversation, file URL, storage file path, message ID, etc.
/// </summary>
[JsonPropertyName("identifier")]
public string Identifier { get; set; }
/// <summary>
/// Gets or sets the content.
/// The content to be processed.
/// </summary>
[JsonPropertyName("content")]
public ContentBase Content { get; set; }
/// <summary>
/// Gets or sets the name.
/// Name of the content, e.g., file name or web page title.
/// </summary>
[JsonPropertyName("name")]
public string Name { get; set; }
/// <summary>
/// Gets or sets the correlationId.
/// Identifier to group multiple contents.
/// </summary>
[JsonPropertyName("correlationId")]
public string? CorrelationId { get; set; }
/// <summary>
/// Gets or sets the sequenceNumber.
/// Sequence in which the content was originally generated.
/// </summary>
[JsonPropertyName("sequenceNumber")]
public long? SequenceNumber { get; set; }
/// <summary>
/// Gets or sets the length.
/// Content length in bytes.
/// </summary>
[JsonPropertyName("length")]
public long? Length { get; set; }
/// <summary>
/// Gets or sets the isTruncated.
/// Indicates if the original content has been truncated, e.g., to meet text or file size limits.
/// </summary>
[JsonPropertyName("isTruncated")]
public bool IsTruncated { get; set; }
/// <summary>
/// Gets or sets the createdDateTime.
/// When the content was created. E.g., file created time or the time when a message was sent.
/// </summary>
[JsonPropertyName("createdDateTime")]
public DateTimeOffset CreatedDateTime { get; set; } = DateTime.UtcNow;
/// <summary>
/// Gets or sets the modifiedDateTime.
/// When the content was last modified. E.g., file last modified time. For content created on the fly, such as messaging, whenModified and whenCreated are expected to be the same.
/// </summary>
[JsonPropertyName("modifiedDateTime")]
public DateTimeOffset? ModifiedDateTime { get; set; } = DateTime.UtcNow;
}
@@ -0,0 +1,46 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Represents metadata for conversation content to be processed by the Purview SDK.
/// </summary>
internal sealed class ProcessConversationMetadata : ProcessContentMetadataBase
{
private const string ProcessConversationMetadataDataType = Constants.ODataGraphNamespace + ".processConversationMetadata";
/// <summary>
/// Initializes a new instance of the <see cref="ProcessConversationMetadata"/> class.
/// </summary>
public ProcessConversationMetadata(ContentBase contentBase, string identifier, bool isTruncated, string name) : base(contentBase, identifier, isTruncated, name)
{
this.DataType = ProcessConversationMetadataDataType;
}
/// <summary>
/// Gets or sets the parent message ID for nested conversations.
/// </summary>
[JsonPropertyName("parentMessageId")]
public string? ParentMessageId { get; set; }
/// <summary>
/// Gets or sets the accessed resources during message generation for bot messages.
/// </summary>
[JsonPropertyName("accessedResources_v2")]
public List<AccessedResourceDetails>? AccessedResources { get; set; }
/// <summary>
/// Gets or sets the plugins used during message generation for bot messages.
/// </summary>
[JsonPropertyName("plugins")]
public List<AIInteractionPlugin>? Plugins { get; set; }
/// <summary>
/// Gets or sets the collection of AI agent information.
/// </summary>
[JsonPropertyName("agents")]
public List<AIAgentInfo>? Agents { get; set; }
}
@@ -0,0 +1,27 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Represents metadata for a file content to be processed by the Purview SDK.
/// </summary>
internal sealed class ProcessFileMetadata : ProcessContentMetadataBase
{
private const string ProcessFileMetadataDataType = Constants.ODataGraphNamespace + ".processFileMetadata";
/// <summary>
/// Initializes a new instance of the <see cref="ProcessFileMetadata"/> class.
/// </summary>
public ProcessFileMetadata(ContentBase contentBase, string identifier, bool isTruncated, string name) : base(contentBase, identifier, isTruncated, name)
{
this.DataType = ProcessFileMetadataDataType;
}
/// <summary>
/// Gets or sets the owner ID.
/// </summary>
[JsonPropertyName("ownerId")]
public string? OwnerId { get; set; }
}
@@ -0,0 +1,24 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Contains information about a processing error.
/// </summary>
internal sealed class ProcessingError : ClassificationErrorBase
{
/// <summary>
/// Details about the error.
/// </summary>
[JsonPropertyName("details")]
public List<ClassificationErrorBase>? Details { get; set; }
/// <summary>
/// Gets or sets the error type.
/// </summary>
[JsonPropertyName("type")]
public ContentProcessingErrorType? Type { get; set; }
}
@@ -0,0 +1,26 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Represents metadata for a protected application that is integrated with Purview.
/// </summary>
internal sealed class ProtectedAppMetadata : IntegratedAppMetadata
{
/// <summary>
/// Creates a new instance of the <see cref="ProtectedAppMetadata"/> class.
/// </summary>
/// <param name="applicationLocation">The location information of the protected app's data.</param>
public ProtectedAppMetadata(PolicyLocation applicationLocation)
{
this.ApplicationLocation = applicationLocation;
}
/// <summary>
/// The location of the application.
/// </summary>
[JsonPropertyName("applicationLocation")]
public PolicyLocation ApplicationLocation { get; set; }
}
@@ -0,0 +1,52 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Activities that can be protected by the Purview Protection Scopes API.
/// </summary>
[Flags]
[DataContract]
[JsonConverter(typeof(JsonStringEnumConverter<ProtectionScopeActivities>))]
internal enum ProtectionScopeActivities
{
/// <summary>
/// None.
/// </summary>
[EnumMember(Value = "none")]
None = 0,
/// <summary>
/// Upload text activity.
/// </summary>
[EnumMember(Value = "uploadText")]
UploadText = 1,
/// <summary>
/// Upload file activity.
/// </summary>
[EnumMember(Value = "uploadFile")]
UploadFile = 2,
/// <summary>
/// Download text activity.
/// </summary>
[EnumMember(Value = "downloadText")]
DownloadText = 4,
/// <summary>
/// Download file activity.
/// </summary>
[EnumMember(Value = "downloadFile")]
DownloadFile = 8,
/// <summary>
/// Unknown future value.
/// </summary>
[EnumMember(Value = "unknownFutureValue")]
UnknownFutureValue = 16
}
@@ -0,0 +1,27 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Indicates status of protection scope changes.
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter<ProtectionScopeState>))]
internal enum ProtectionScopeState
{
/// <summary>
/// Scope state hasn't changed.
/// </summary>
NotModified = 0,
/// <summary>
/// Scope state has changed.
/// </summary>
Modified = 1,
/// <summary>
/// Unknown value placeholder for future use.
/// </summary>
UnknownFutureValue = 2
}
@@ -0,0 +1,91 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Linq;
using Microsoft.Agents.AI.Purview.Models.Requests;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// A cache key for storing protection scope responses.
/// </summary>
internal sealed class ProtectionScopesCacheKey
{
/// <summary>
/// Creates a new instance of <see cref="ProtectionScopesCacheKey"/>.
/// </summary>
/// <param name="userId">The entra id of the user who made the interaction.</param>
/// <param name="tenantId">The tenant id of the user who made the interaction.</param>
/// <param name="activities">The activity performed with the data.</param>
/// <param name="location">The location where the data came from.</param>
/// <param name="pivotOn">The property to pivot on.</param>
/// <param name="deviceMetadata">Metadata about the device that made the interaction.</param>
/// <param name="integratedAppMetadata">Metadata about the app that is integrating with Purview.</param>
public ProtectionScopesCacheKey(
string userId,
string tenantId,
ProtectionScopeActivities activities,
PolicyLocation? location,
PolicyPivotProperty? pivotOn,
DeviceMetadata? deviceMetadata,
IntegratedAppMetadata? integratedAppMetadata)
{
this.UserId = userId;
this.TenantId = tenantId;
this.Activities = activities;
this.Location = location;
this.PivotOn = pivotOn;
this.DeviceMetadata = deviceMetadata;
this.IntegratedAppMetadata = integratedAppMetadata;
}
/// <summary>
/// Creates a mew instance of <see cref="ProtectionScopesCacheKey"/>.
/// </summary>
/// <param name="request">A protection scopes request.</param>
public ProtectionScopesCacheKey(
ProtectionScopesRequest request) : this(
request.UserId,
request.TenantId,
request.Activities,
request.Locations.FirstOrDefault(),
request.PivotOn,
request.DeviceMetadata,
request.IntegratedAppMetadata)
{
}
/// <summary>
/// The id of the user making the request.
/// </summary>
public string UserId { get; set; }
/// <summary>
/// The id of the tenant containing the user making the request.
/// </summary>
public string TenantId { get; set; }
/// <summary>
/// The activity performed with the content.
/// </summary>
public ProtectionScopeActivities Activities { get; set; }
/// <summary>
/// The location of the application.
/// </summary>
public PolicyLocation? Location { get; set; }
/// <summary>
/// The property used to pivot the policy evaluation.
/// </summary>
public PolicyPivotProperty? PivotOn { get; set; }
/// <summary>
/// Metadata about the device used to access the content.
/// </summary>
public DeviceMetadata? DeviceMetadata { get; set; }
/// <summary>
/// Metadata about the integrated app used to access the content.
/// </summary>
public IntegratedAppMetadata? IntegratedAppMetadata { get; set; }
}
@@ -0,0 +1,28 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Represents a binary content item to be processed.
/// </summary>
internal sealed class PurviewBinaryContent : ContentBase
{
private const string BinaryContentDataType = Constants.ODataGraphNamespace + ".binaryContent";
/// <summary>
/// Initializes a new instance of the <see cref="PurviewBinaryContent"/> class.
/// </summary>
/// <param name="data">The binary content in byte array format.</param>
public PurviewBinaryContent(byte[] data) : base(BinaryContentDataType)
{
this.Data = data;
}
/// <summary>
/// Gets or sets the binary data.
/// </summary>
[JsonPropertyName("data")]
public byte[] Data { get; set; }
}
@@ -0,0 +1,28 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Represents a text content item to be processed.
/// </summary>
internal sealed class PurviewTextContent : ContentBase
{
private const string TextContentDataType = Constants.ODataGraphNamespace + ".textContent";
/// <summary>
/// Initializes a new instance of the <see cref="PurviewTextContent"/> class.
/// </summary>
/// <param name="data">The text content in string format.</param>
public PurviewTextContent(string data) : base(TextContentDataType)
{
this.Data = data;
}
/// <summary>
/// Gets or sets the text data.
/// </summary>
[JsonPropertyName("data")]
public string Data { get; set; }
}
@@ -0,0 +1,32 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Status of the access operation.
/// </summary>
[DataContract]
[JsonConverter(typeof(JsonStringEnumConverter<ResourceAccessStatus>))]
internal enum ResourceAccessStatus
{
/// <summary>
/// Represents failed access to the resource.
/// </summary>
[EnumMember(Value = "failure")]
Failure = 0,
/// <summary>
/// Represents successful access to the resource.
/// </summary>
[EnumMember(Value = "success")]
Success = 1,
/// <summary>
/// Unknown future value.
/// </summary>
[EnumMember(Value = "unknownFutureValue")]
UnknownFutureValue = 2
}
@@ -0,0 +1,46 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Access type performed on the resource.
/// </summary>
[Flags]
[DataContract]
[JsonConverter(typeof(JsonStringEnumConverter<ResourceAccessType>))]
internal enum ResourceAccessType : long
{
/// <summary>
/// No access type.
/// </summary>
[EnumMember(Value = "none")]
None = 0,
/// <summary>
/// Read access.
/// </summary>
[EnumMember(Value = "read")]
Read = 1 << 0,
/// <summary>
/// Write access.
/// </summary>
[EnumMember(Value = "write")]
Write = 1 << 1,
/// <summary>
/// Create access.
/// </summary>
[EnumMember(Value = "create")]
Create = 1 << 2,
/// <summary>
/// Unknown future value.
/// </summary>
[EnumMember(Value = "unknownFutureValue")]
UnknownFutureValue = 1 << 3
}
@@ -0,0 +1,32 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Restriction actions for devices.
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter<RestrictionAction>))]
internal enum RestrictionAction
{
/// <summary>
/// Warn Action.
/// </summary>
Warn,
/// <summary>
/// Audit action.
/// </summary>
Audit,
/// <summary>
/// Block action.
/// </summary>
Block,
/// <summary>
/// Allow action
/// </summary>
Allow
}
@@ -0,0 +1,23 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json.Serialization;
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Represents tenant/user/group scopes.
/// </summary>
internal sealed class Scope
{
/// <summary>
/// The odata type of the scope used to identify what type of scope was returned.
/// </summary>
[JsonPropertyName("@odata.type")]
public string? ODataType { get; set; }
/// <summary>
/// Gets or sets the scope identifier.
/// </summary>
[JsonPropertyName("identity")]
public string? Identity { get; set; }
}
@@ -0,0 +1,29 @@
// Copyright (c) Microsoft. All rights reserved.
namespace Microsoft.Agents.AI.Purview.Models.Common;
/// <summary>
/// Info pulled from an auth token.
/// </summary>
internal sealed class TokenInfo
{
/// <summary>
/// The entra id of the authenticated user. This is null if the auth token is not a user token.
/// </summary>
public string? UserId { get; set; }
/// <summary>
/// The tenant id of the auth token.
/// </summary>
public string? TenantId { get; set; }
/// <summary>
/// The client id of the auth token.
/// </summary>
public string? ClientId { get; set; }
/// <summary>
/// Gets a value indicating whether the token is associated with a user.
/// </summary>
public bool IsUserToken => !string.IsNullOrEmpty(this.UserId);
}
@@ -0,0 +1,10 @@
// Copyright (c) Microsoft. All rights reserved.
namespace Microsoft.Agents.AI.Purview.Models.Jobs;
/// <summary>
/// Abstract base class for background jobs.
/// </summary>
internal abstract class BackgroundJobBase
{
}
@@ -0,0 +1,25 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.AI.Purview.Models.Requests;
namespace Microsoft.Agents.AI.Purview.Models.Jobs;
/// <summary>
/// Class representing a job to send content activities to the Purview service.
/// </summary>
internal sealed class ContentActivityJob : BackgroundJobBase
{
/// <summary>
/// Create a new instance of the <see cref="ContentActivityJob"/> class.
/// </summary>
/// <param name="request">The content activities request to be sent in the background.</param>
public ContentActivityJob(ContentActivitiesRequest request)
{
this.Request = request;
}
/// <summary>
/// The request to send to the Purview service.
/// </summary>
public ContentActivitiesRequest Request { get; }
}
@@ -0,0 +1,25 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.AI.Purview.Models.Requests;
namespace Microsoft.Agents.AI.Purview.Models.Jobs;
/// <summary>
/// Class representing a job to process content.
/// </summary>
internal sealed class ProcessContentJob : BackgroundJobBase
{
/// <summary>
/// Initializes a new instance of the <see cref="ProcessContentJob"/> class.
/// </summary>
/// <param name="request">The process content request to be sent in the background.</param>
public ProcessContentJob(ProcessContentRequest request)
{
this.Request = request;
}
/// <summary>
/// The request to process content.
/// </summary>
public ProcessContentRequest Request { get; }
}
@@ -0,0 +1,66 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Text.Json.Serialization;
using Microsoft.Agents.AI.Purview.Models.Common;
namespace Microsoft.Agents.AI.Purview.Models.Requests;
/// <summary>
/// A request class used for contentActivity requests.
/// </summary>
internal sealed class ContentActivitiesRequest
{
/// <summary>
/// Initializes a new instance of the <see cref="ContentActivitiesRequest"/> class.
/// </summary>
/// <param name="userId">The entra id of the user who performed the activity.</param>
/// <param name="tenantId">The tenant id of the user who performed the activity.</param>
/// <param name="contentMetadata">The metadata about the content that was sent.</param>
/// <param name="correlationId">The correlation id of the request.</param>
/// <param name="scopeIdentifier">The scope identifier of the protection scopes associated with this request.</param>
public ContentActivitiesRequest(string userId, string tenantId, ContentToProcess contentMetadata, Guid correlationId = default, string? scopeIdentifier = null)
{
this.UserId = userId ?? throw new ArgumentNullException(nameof(userId));
this.TenantId = tenantId ?? throw new ArgumentNullException(nameof(tenantId));
this.ContentMetadata = contentMetadata ?? throw new ArgumentNullException(nameof(contentMetadata));
this.CorrelationId = correlationId == default ? Guid.NewGuid() : correlationId;
this.ScopeIdentifier = scopeIdentifier;
}
/// <summary>
/// Gets or sets the ID of the signal.
/// </summary>
[JsonPropertyName("id")]
public string Id { get; set; } = Guid.NewGuid().ToString();
/// <summary>
/// Gets or sets the user ID of the content that is generating the signal.
/// </summary>
[JsonPropertyName("userId")]
public string UserId { get; set; }
/// <summary>
/// Gets or sets the scope identifier for the signal.
/// </summary>
[JsonPropertyName("scopeIdentifier")]
public string? ScopeIdentifier { get; set; }
/// <summary>
/// Gets or sets the content and associated content metadata for the content used to generate the signal.
/// </summary>
[JsonPropertyName("contentMetadata")]
public ContentToProcess ContentMetadata { get; set; }
/// <summary>
/// Gets or sets the correlation ID for the signal.
/// </summary>
[JsonIgnore]
public Guid CorrelationId { get; set; }
/// <summary>
/// Gets or sets the tenant id for the signal.
/// </summary>
[JsonIgnore]
public string TenantId { get; set; }
}
@@ -0,0 +1,56 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Text.Json.Serialization;
using Microsoft.Agents.AI.Purview.Models.Common;
namespace Microsoft.Agents.AI.Purview.Models.Requests;
/// <summary>
/// Request for ProcessContent API
/// </summary>
internal sealed class ProcessContentRequest
{
/// <summary>
/// Creates a new instance of ProcessContentRequest.
/// </summary>
/// <param name="contentToProcess">The content and its metadata that will be processed.</param>
/// <param name="userId">The entra user id of the user making the request.</param>
/// <param name="tenantId">The tenant id of the user making the request.</param>
public ProcessContentRequest(ContentToProcess contentToProcess, string userId, string tenantId)
{
this.ContentToProcess = contentToProcess;
this.UserId = userId;
this.TenantId = tenantId;
}
/// <summary>
/// The content to process.
/// </summary>
[JsonPropertyName("contentToProcess")]
public ContentToProcess ContentToProcess { get; set; }
/// <summary>
/// The user id of the user making the request.
/// </summary>
[JsonIgnore]
public string UserId { get; set; }
/// <summary>
/// The correlation id of the request.
/// </summary>
[JsonIgnore]
public Guid CorrelationId { get; set; } = Guid.NewGuid();
/// <summary>
/// The tenant id of the user making the request.
/// </summary>
[JsonIgnore]
public string TenantId { get; set; }
/// <summary>
/// The identifier of the cached protection scopes.
/// </summary>
[JsonIgnore]
internal string? ScopeIdentifier { get; set; }
}
@@ -0,0 +1,86 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
using Microsoft.Agents.AI.Purview.Models.Common;
namespace Microsoft.Agents.AI.Purview.Models.Requests;
/// <summary>
/// Request model for user protection scopes requests.
/// </summary>
[DataContract]
internal sealed class ProtectionScopesRequest
{
/// <summary>
/// Creates a new instance of ProtectionScopesRequest.
/// </summary>
/// <param name="userId">The entra id of the user who made the interaction.</param>
/// <param name="tenantId">The tenant id of the user who made the interaction.</param>
public ProtectionScopesRequest(string userId, string tenantId)
{
this.UserId = userId;
this.TenantId = tenantId;
}
/// <summary>
/// Activities to include in the scope
/// </summary>
[DataMember]
[JsonPropertyName("activities")]
public ProtectionScopeActivities Activities { get; set; }
/// <summary>
/// Gets or sets the locations to compute protection scopes for.
/// </summary>
[JsonPropertyName("locations")]
public ICollection<PolicyLocation> Locations { get; set; } = Array.Empty<PolicyLocation>();
/// <summary>
/// Response aggregation pivot
/// </summary>
[DataMember]
[JsonPropertyName("pivotOn")]
public PolicyPivotProperty? PivotOn { get; set; }
/// <summary>
/// Device metadata
/// </summary>
[DataMember]
[JsonPropertyName("deviceMetadata")]
public DeviceMetadata? DeviceMetadata { get; set; }
/// <summary>
/// Integrated app metadata
/// </summary>
[DataMember]
[JsonPropertyName("integratedAppMetadata")]
public IntegratedAppMetadata? IntegratedAppMetadata { get; set; }
/// <summary>
/// The correlation id of the request.
/// </summary>
[JsonIgnore]
public Guid CorrelationId { get; set; } = Guid.NewGuid();
/// <summary>
/// Scope ID, used to detect stale client scoping information
/// </summary>
[DataMember]
[JsonIgnore]
public string ScopeIdentifier { get; set; } = string.Empty;
/// <summary>
/// The id of the user making the request.
/// </summary>
[JsonIgnore]
public string UserId { get; set; }
/// <summary>
/// The tenant id of the user making the request.
/// </summary>
[JsonIgnore]
public string TenantId { get; set; }
}
@@ -0,0 +1,25 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Net;
using System.Text.Json.Serialization;
using Microsoft.Agents.AI.Purview.Models.Common;
namespace Microsoft.Agents.AI.Purview.Models.Responses;
/// <summary>
/// Represents the response for content activities requests.
/// </summary>
internal sealed class ContentActivitiesResponse
{
/// <summary>
/// Gets or sets the HTTP status code associated with the response.
/// </summary>
[JsonIgnore]
public HttpStatusCode StatusCode { get; set; }
/// <summary>
/// Details about any errors returned by the request.
/// </summary>
[JsonPropertyName("error")]
public ErrorDetails? Error { get; set; }
}
@@ -0,0 +1,42 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
using Microsoft.Agents.AI.Purview.Models.Common;
namespace Microsoft.Agents.AI.Purview.Models.Responses;
/// <summary>
/// The response of a process content evaluation.
/// </summary>
internal sealed class ProcessContentResponse
{
/// <summary>
/// Gets or sets the evaluation id.
/// </summary>
[Key]
public string? Id { get; set; }
/// <summary>
/// Gets or sets the status of protection scope changes.
/// </summary>
[DataMember]
[JsonPropertyName("protectionScopeState")]
public ProtectionScopeState? ProtectionScopeState { get; set; }
/// <summary>
/// Gets or sets the policy actions to take.
/// </summary>
[DataMember]
[JsonPropertyName("policyActions")]
public IReadOnlyList<DlpActionInfo>? PolicyActions { get; set; }
/// <summary>
/// Gets or sets error information about the evaluation.
/// </summary>
[DataMember]
[JsonPropertyName("processingErrors")]
public IReadOnlyList<ProcessingError>? ProcessingErrors { get; set; }
}
@@ -0,0 +1,24 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Collections.Generic;
using System.Text.Json.Serialization;
using Microsoft.Agents.AI.Purview.Models.Common;
namespace Microsoft.Agents.AI.Purview.Models.Responses;
/// <summary>
/// A response object containing protection scopes for a tenant.
/// </summary>
internal sealed class ProtectionScopesResponse
{
/// <summary>
/// The identifier used for caching the user protection scopes.
/// </summary>
public string? ScopeIdentifier { get; set; }
/// <summary>
/// The user protection scopes.
/// </summary>
[JsonPropertyName("value")]
public IReadOnlyCollection<PolicyScopeBase>? Scopes { get; set; }
}