From cb9894975ff60bdabe7e388a4a6868f578816b83 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Fri, 19 May 2023 08:51:11 +0800 Subject: [PATCH] Add debugging attributes to System.Security.Claims types (#86424) * Add debugging attributes to System.Security.Claims types * Update * Update ref, add type proxies * PR feedback * PR feedback --- .../ref/System.Security.Claims.cs | 6 ++-- .../src/System/Security/Claims/ClaimsIdentity.cs | 36 +++++++++++++++++++ .../src/System/Security/Claims/ClaimsPrincipal.cs | 42 ++++++++++++++++++++++ 3 files changed, 81 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Security.Claims/ref/System.Security.Claims.cs b/src/libraries/System.Security.Claims/ref/System.Security.Claims.cs index 4bc8d68..2d3b6bd 100644 --- a/src/libraries/System.Security.Claims/ref/System.Security.Claims.cs +++ b/src/libraries/System.Security.Claims/ref/System.Security.Claims.cs @@ -41,11 +41,11 @@ namespace System.Security.Claims public ClaimsIdentity(System.Collections.Generic.IEnumerable? claims, string? authenticationType) { } public ClaimsIdentity(System.Collections.Generic.IEnumerable? claims, string? authenticationType, string? nameType, string? roleType) { } public ClaimsIdentity(System.IO.BinaryReader reader) { } - [System.ObsoleteAttribute("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.", DiagnosticId = "SYSLIB0051", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.", DiagnosticId="SYSLIB0051", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] protected ClaimsIdentity(System.Runtime.Serialization.SerializationInfo info) { } - [System.ObsoleteAttribute("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.", DiagnosticId = "SYSLIB0051", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.", DiagnosticId="SYSLIB0051", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] protected ClaimsIdentity(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } protected ClaimsIdentity(System.Security.Claims.ClaimsIdentity other) { } public ClaimsIdentity(System.Security.Principal.IIdentity? identity) { } @@ -84,8 +84,8 @@ namespace System.Security.Claims public ClaimsPrincipal() { } public ClaimsPrincipal(System.Collections.Generic.IEnumerable identities) { } public ClaimsPrincipal(System.IO.BinaryReader reader) { } - [System.ObsoleteAttribute("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.", DiagnosticId = "SYSLIB0051", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + [System.ObsoleteAttribute("This API supports obsolete formatter-based serialization. It should not be called or extended by application code.", DiagnosticId="SYSLIB0051", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] protected ClaimsPrincipal(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } public ClaimsPrincipal(System.Security.Principal.IIdentity identity) { } public ClaimsPrincipal(System.Security.Principal.IPrincipal principal) { } diff --git a/src/libraries/System.Security.Claims/src/System/Security/Claims/ClaimsIdentity.cs b/src/libraries/System.Security.Claims/src/System/Security/Claims/ClaimsIdentity.cs index 9733ca8..c928dd8 100644 --- a/src/libraries/System.Security.Claims/src/System/Security/Claims/ClaimsIdentity.cs +++ b/src/libraries/System.Security.Claims/src/System/Security/Claims/ClaimsIdentity.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics; using System.IO; using System.Runtime.Serialization; using System.Security.Principal; @@ -12,6 +13,8 @@ namespace System.Security.Claims /// /// An Identity that is represented by a set of claims. /// + [DebuggerDisplay("{DebuggerToString(),nq}")] + [DebuggerTypeProxy(typeof(ClaimsIdentityDebugProxy))] public class ClaimsIdentity : IIdentity { private enum SerializationMask @@ -933,5 +936,38 @@ namespace System.Security.Claims { throw new PlatformNotSupportedException(); } + + internal string DebuggerToString() + { + // DebuggerDisplayAttribute is inherited. Use virtual members instead of private fields to gather data. + int claimsCount = 0; + foreach (Claim item in Claims) + { + claimsCount++; + } + + return $"Identity Name = {Name ?? "(null)"}, IsAuthenticated = {IsAuthenticated}, Claims Count = {claimsCount}"; + } + + private sealed class ClaimsIdentityDebugProxy + { + private readonly ClaimsIdentity _identity; + + public ClaimsIdentityDebugProxy(ClaimsIdentity identity) + { + _identity = identity; + } + + public ClaimsIdentity? Actor => _identity.Actor; + public string? AuthenticationType => _identity.AuthenticationType; + public object? BootstrapContext => _identity.BootstrapContext; + // List type has a friendly debugger view + public List Claims => new List(_identity.Claims); + public bool IsAuthenticated => _identity.IsAuthenticated; + public string? Label => _identity.Label; + public string? Name => _identity.Name; + public string NameClaimType => _identity.NameClaimType; + public string RoleClaimType => _identity.RoleClaimType; + } } } diff --git a/src/libraries/System.Security.Claims/src/System/Security/Claims/ClaimsPrincipal.cs b/src/libraries/System.Security.Claims/src/System/Security/Claims/ClaimsPrincipal.cs index a423bfa..1e1efd8 100644 --- a/src/libraries/System.Security.Claims/src/System/Security/Claims/ClaimsPrincipal.cs +++ b/src/libraries/System.Security.Claims/src/System/Security/Claims/ClaimsPrincipal.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics; using System.IO; using System.Runtime.Serialization; using System.Security.Principal; @@ -13,6 +14,8 @@ namespace System.Security.Claims /// /// Concrete IPrincipal supporting multiple claims-based identities /// + [DebuggerDisplay("{DebuggerToString(),nq}")] + [DebuggerTypeProxy(typeof(ClaimsPrincipalDebugProxy))] public class ClaimsPrincipal : IPrincipal { private enum SerializationMask @@ -567,5 +570,44 @@ namespace System.Security.Claims { throw new PlatformNotSupportedException(); } + + private string DebuggerToString() + { + // DebuggerDisplayAttribute is inherited. Use virtual members instead of private fields to gather data. + int identitiesCount = 0; + foreach (ClaimsIdentity items in Identities) + { + identitiesCount++; + } + + int claimsCount = 0; + foreach (Claim item in Claims) + { + claimsCount++; + } + + // Return debug string optimized for the case of one identity. + if (identitiesCount == 1 && Identity is ClaimsIdentity claimsIdentity) + { + return $"Principal {claimsIdentity.DebuggerToString()}"; + } + + return $"Principal Identities Count: {identitiesCount}, Claims Count: {claimsCount}"; + } + + private sealed class ClaimsPrincipalDebugProxy + { + private readonly ClaimsPrincipal _principal; + + public ClaimsPrincipalDebugProxy(ClaimsPrincipal principal) + { + _principal = principal; + } + + // List type has a friendly debugger view + public List Claims => new List(_principal.Claims); + public List Identities => new List(_principal.Identities); + public IIdentity? Identity => _principal.Identity; + } } } -- 2.7.4