Updating ClaimsPrincipal.Current to default back to Thread.CurrentPrincipal, as was...
authorTanner Gooding <tagoo@outlook.com>
Thu, 13 Aug 2020 13:35:32 +0000 (06:35 -0700)
committerGitHub <noreply@github.com>
Thu, 13 Aug 2020 13:35:32 +0000 (08:35 -0500)
* Updating ClaimsPrincipal.Current to default back to Thread.CurrentPrincipal, as was done on .NET Framework

* Add test that ClaimsPrincipal.Current falls back to Thread.CurrentPrincipal to match .NET Framework.

Co-authored-by: Eric Erhardt <eric.erhardt@microsoft.com>
src/libraries/System.Security.Claims/src/System.Security.Claims.csproj
src/libraries/System.Security.Claims/src/System/Security/Claims/ClaimsPrincipal.cs
src/libraries/System.Security.Claims/tests/ClaimsPrincipalTests.cs
src/libraries/System.Security.Claims/tests/System.Security.Claims.Tests.csproj

index ca146c4..ca4bd80 100644 (file)
@@ -17,5 +17,6 @@
     <Reference Include="System.Runtime" />
     <Reference Include="System.Runtime.Extensions" />
     <Reference Include="System.Security.Principal" />
+    <Reference Include="System.Threading.Thread" />
   </ItemGroup>
 </Project>
index f7e4aa8..6762cdd 100644 (file)
@@ -5,6 +5,7 @@ using System.Collections.Generic;
 using System.IO;
 using System.Runtime.Serialization;
 using System.Security.Principal;
+using System.Threading;
 
 namespace System.Security.Claims
 {
@@ -26,6 +27,11 @@ namespace System.Security.Claims
         private static Func<IEnumerable<ClaimsIdentity>, ClaimsIdentity?> s_identitySelector = SelectPrimaryIdentity;
         private static Func<ClaimsPrincipal> s_principalSelector = ClaimsPrincipalSelector;
 
+        private static ClaimsPrincipal SelectClaimsPrincipal()
+        {
+            return (Thread.CurrentPrincipal is ClaimsPrincipal claimsPrincipal) ? claimsPrincipal : new ClaimsPrincipal(Thread.CurrentPrincipal!);
+        }
+
         protected ClaimsPrincipal(SerializationInfo info, StreamingContext context)
         {
             throw new PlatformNotSupportedException();
@@ -280,12 +286,7 @@ namespace System.Security.Claims
             // just accesses the current selected principal selector, doesn't set
             get
             {
-                if (s_principalSelector != null)
-                {
-                    return s_principalSelector();
-                }
-
-                return null;
+                return s_principalSelector is not null ? s_principalSelector() : SelectClaimsPrincipal();
             }
         }
 
index 81f3420..c28b770 100644 (file)
@@ -5,6 +5,8 @@ using System.Collections.Generic;
 using System.IO;
 using System.Linq;
 using System.Security.Principal;
+using System.Threading;
+using Microsoft.DotNet.RemoteExecutor;
 using Xunit;
 
 namespace System.Security.Claims
@@ -200,6 +202,35 @@ namespace System.Security.Claims
             AssertExtensions.Throws<ArgumentNullException>("reader", () => new ClaimsPrincipal((BinaryReader)null));
         }
 
+        [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+        public void Current_FallsBackToThread()
+        {
+            RemoteExecutor.Invoke(() =>
+            {
+                ClaimsPrincipal principal1 = new ClaimsPrincipal();
+                ClaimsPrincipal principal2 = new ClaimsPrincipal();
+
+                Thread.CurrentPrincipal = principal1;
+                Assert.Same(principal1, ClaimsPrincipal.Current);
+
+                Thread.CurrentPrincipal = principal2;
+                Assert.Same(principal2, ClaimsPrincipal.Current);
+
+                NonClaimsIdentity id = new NonClaimsIdentity() { Name = "NonClaimsIdentity_Name" };
+                NonClaimsPrincipal nonClaimsPrincipal = new NonClaimsPrincipal() { Identity = id };
+
+                Thread.CurrentPrincipal = nonClaimsPrincipal;
+
+                ClaimsPrincipal current = ClaimsPrincipal.Current;
+                Assert.NotNull(current);
+                Assert.Equal("NonClaimsIdentity_Name", current.Identity.Name);
+
+                // match .NET Framework behavior by throwing ArgumentNullException when Thread.CurrentPrincipal is null
+                Thread.CurrentPrincipal = null;
+                Assert.Throws<ArgumentNullException>(() => ClaimsPrincipal.Current);
+            }).Dispose();
+        }
+
         private class NonClaimsPrincipal : IPrincipal
         {
             public IIdentity Identity { get; set; }
index cbdc509..74dc037 100644 (file)
@@ -1,5 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
+    <IncludeRemoteExecutor>true</IncludeRemoteExecutor>
     <TargetFrameworks>$(NetCoreAppCurrent)</TargetFrameworks>
   </PropertyGroup>
   <ItemGroup>