Skip to content

Commit 7a309db

Browse files
Merge pull request #3667 from rockfordlhotka/v8-work
Support .NET 7 context behavior
2 parents 628bdec + 86a7dfb commit 7a309db

File tree

2 files changed

+233
-2
lines changed

2 files changed

+233
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
#if NET5_0_OR_GREATER
2+
//-----------------------------------------------------------------------
3+
// <copyright file="ApplicationContextManagerInMemory.cs" company="Marimer LLC">
4+
// Copyright (c) Marimer LLC. All rights reserved.
5+
// Website: https://cslanet.com
6+
// </copyright>
7+
// <summary>Application context manager that uses HttpContextAccessor</summary>
8+
//-----------------------------------------------------------------------
9+
using Csla.Core;
10+
using Microsoft.AspNetCore.Components.Authorization;
11+
using System;
12+
using System.Security.Claims;
13+
using System.Security.Principal;
14+
using System.Threading.Tasks;
15+
16+
namespace Csla.AspNetCore.Blazor
17+
{
18+
/// <summary>
19+
/// Application context manager that uses HttpContextAccessor when
20+
/// resolving HttpContext to store context values.
21+
/// </summary>
22+
public class ApplicationContextManagerInMemory : IContextManager, IDisposable
23+
{
24+
private ContextDictionary LocalContext { get; set; }
25+
private ContextDictionary ClientContext { get; set; }
26+
private IPrincipal CurrentPrincipal { get; set; }
27+
private readonly ClaimsPrincipal UnauthenticatedPrincipal = new();
28+
private bool disposedValue;
29+
30+
/// <summary>
31+
/// Gets the current HttpContext instance.
32+
/// </summary>
33+
protected AuthenticationStateProvider AuthenticationStateProvider { get; private set; }
34+
35+
/// <summary>
36+
/// Gets or sets a reference to the current ApplicationContext.
37+
/// </summary>
38+
public ApplicationContext ApplicationContext { get; set; }
39+
40+
/// <summary>
41+
/// Gets the active circuit state.
42+
/// </summary>
43+
protected ActiveCircuitState ActiveCircuitState { get; private set; }
44+
45+
/// <summary>
46+
/// Creates an instance of the object, initializing it
47+
/// with the required IServiceProvider.
48+
/// </summary>
49+
/// <param name="authenticationStateProvider">AuthenticationStateProvider service</param>
50+
/// <param name="activeCircuitState"></param>
51+
public ApplicationContextManagerInMemory(AuthenticationStateProvider authenticationStateProvider, ActiveCircuitState activeCircuitState)
52+
{
53+
AuthenticationStateProvider = authenticationStateProvider;
54+
ActiveCircuitState = activeCircuitState;
55+
CurrentPrincipal = UnauthenticatedPrincipal;
56+
AuthenticationStateProvider.AuthenticationStateChanged += AuthenticationStateProvider_AuthenticationStateChanged;
57+
InitializeUser();
58+
}
59+
60+
private void InitializeUser()
61+
{
62+
Task<AuthenticationState> task = default;
63+
try
64+
{
65+
task = AuthenticationStateProvider.GetAuthenticationStateAsync();
66+
}
67+
catch (InvalidOperationException ex)
68+
{
69+
task = Task.FromResult(new AuthenticationState((ClaimsPrincipal)UnauthenticatedPrincipal));
70+
string message = ex.Message;
71+
if (message.Contains(nameof(AuthenticationStateProvider.GetAuthenticationStateAsync))
72+
&& message.Contains(nameof(IHostEnvironmentAuthenticationStateProvider.SetAuthenticationState)))
73+
{
74+
SetHostPrincipal(task);
75+
}
76+
else
77+
{
78+
throw;
79+
}
80+
}
81+
AuthenticationStateProvider_AuthenticationStateChanged(task);
82+
}
83+
84+
private void AuthenticationStateProvider_AuthenticationStateChanged(Task<AuthenticationState> task)
85+
{
86+
if (task is null)
87+
{
88+
CurrentPrincipal = UnauthenticatedPrincipal;
89+
}
90+
else
91+
{
92+
task.ContinueWith((t) =>
93+
{
94+
if (task.IsCompletedSuccessfully && task.Result != null)
95+
CurrentPrincipal = task.Result.User;
96+
else
97+
CurrentPrincipal = UnauthenticatedPrincipal;
98+
});
99+
}
100+
}
101+
102+
/// <summary>
103+
/// Gets a value indicating whether this
104+
/// context manager is valid for use in
105+
/// the current environment.
106+
/// </summary>
107+
public bool IsValid
108+
{
109+
get { return ActiveCircuitState.CircuitExists; }
110+
}
111+
112+
/// <summary>
113+
/// Gets a value indicating whether the context manager
114+
/// is stateful.
115+
/// </summary>
116+
public bool IsStatefulContext => true;
117+
118+
/// <summary>
119+
/// Gets the current principal.
120+
/// </summary>
121+
public IPrincipal GetUser()
122+
{
123+
return CurrentPrincipal;
124+
}
125+
126+
/// <summary>
127+
/// Attempts to set the current principal on the registered
128+
/// IHostEnvironmentAuthenticationStateProvider service.
129+
/// </summary>
130+
/// <param name="principal">Principal object.</param>
131+
public virtual void SetUser(IPrincipal principal)
132+
{
133+
if (!ReferenceEquals(CurrentPrincipal, principal))
134+
{
135+
if (principal is ClaimsPrincipal claimsPrincipal)
136+
{
137+
CurrentPrincipal = principal;
138+
SetHostPrincipal(Task.FromResult(new AuthenticationState(claimsPrincipal)));
139+
}
140+
else
141+
{
142+
throw new ArgumentException("typeof(principal) != ClaimsPrincipal");
143+
}
144+
}
145+
}
146+
147+
private void SetHostPrincipal(Task<AuthenticationState> task)
148+
{
149+
if (AuthenticationStateProvider is IHostEnvironmentAuthenticationStateProvider hostProvider)
150+
hostProvider.SetAuthenticationState(task);
151+
}
152+
153+
/// <summary>
154+
/// Gets the local context.
155+
/// </summary>
156+
public ContextDictionary GetLocalContext()
157+
{
158+
if (LocalContext == null)
159+
LocalContext = new ContextDictionary();
160+
return LocalContext;
161+
}
162+
163+
/// <summary>
164+
/// Sets the local context.
165+
/// </summary>
166+
/// <param name="localContext">Local context.</param>
167+
public void SetLocalContext(ContextDictionary localContext)
168+
{
169+
LocalContext = localContext;
170+
}
171+
172+
/// <summary>
173+
/// Gets the client context.
174+
/// </summary>
175+
/// <param name="executionLocation"></param>
176+
public ContextDictionary GetClientContext(ApplicationContext.ExecutionLocations executionLocation)
177+
{
178+
if (ClientContext == null)
179+
ClientContext = new ContextDictionary();
180+
return ClientContext;
181+
}
182+
183+
/// <summary>
184+
/// Sets the client context.
185+
/// </summary>
186+
/// <param name="clientContext">Client context.</param>
187+
/// <param name="executionLocation"></param>
188+
public void SetClientContext(ContextDictionary clientContext, ApplicationContext.ExecutionLocations executionLocation)
189+
{
190+
ClientContext = clientContext;
191+
}
192+
193+
/// <summary>
194+
/// Dispose this object's resources.
195+
/// </summary>
196+
/// <param name="disposing"></param>
197+
protected virtual void Dispose(bool disposing)
198+
{
199+
if (!disposedValue)
200+
{
201+
if (disposing)
202+
{
203+
AuthenticationStateProvider.AuthenticationStateChanged -= AuthenticationStateProvider_AuthenticationStateChanged;
204+
}
205+
disposedValue = true;
206+
}
207+
}
208+
209+
/// <summary>
210+
/// Dispose this object's resources.
211+
/// </summary>
212+
public void Dispose()
213+
{
214+
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
215+
Dispose(disposing: true);
216+
GC.SuppressFinalize(this);
217+
}
218+
}
219+
}
220+
#endif

Source/Csla.Blazor/ConfigurationExtensions.cs

+13-2
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,14 @@ public static CslaOptions AddServerSideBlazor(this CslaOptions config, Action<Bl
4545
// minimize PropertyChanged events
4646
config.BindingOptions.PropertyChangedMode = ApplicationContext.PropertyChangedModes.Windows;
4747

48-
var managerType = Type.GetType("Csla.AspNetCore.Blazor.ApplicationContextManagerBlazor,Csla.AspNetCore");
48+
string managerTypeName;
49+
if (blazorOptions.UseInMemoryApplicationContextManager)
50+
managerTypeName = "Csla.AspNetCore.Blazor.ApplicationContextManagerInMemory,Csla.AspNetCore";
51+
else
52+
managerTypeName = "Csla.AspNetCore.Blazor.ApplicationContextManagerBlazor,Csla.AspNetCore";
53+
var managerType = Type.GetType(managerTypeName);
4954
if (managerType is null)
50-
throw new TypeLoadException("Csla.AspNetCore.Blazor.ApplicationContextManagerBlazor,Csla.AspNetCore");
55+
throw new TypeLoadException(managerTypeName);
5156
var contextManagerType = typeof(Core.IContextManager);
5257
var managers = config.Services.Where(i => i.ServiceType.Equals(contextManagerType)).ToList();
5358
foreach ( var manager in managers )
@@ -82,6 +87,12 @@ public class BlazorServerConfigurationOptions
8287
/// </summary>
8388
public bool UseCslaPermissionsPolicy { get; set; } = true;
8489

90+
/// <summary>
91+
/// Gets or sets a value indicating whether to use the
92+
/// pre-Blazor 8 in-memory context manager.
93+
/// </summary>
94+
public bool UseInMemoryApplicationContextManager { get; set; }
95+
8596
/// <summary>
8697
/// Gets or sets the type of the ISessionManager service.
8798
/// </summary>

0 commit comments

Comments
 (0)