parent
9b6f10d20c
commit
8b7fcf1f76
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Microsoft.AspNetCore.Components.Server.Circuits
|
||||
|
|
@ -11,6 +12,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
HttpContext httpContext,
|
||||
CircuitClientProxy client,
|
||||
string uriAbsolute,
|
||||
string baseUriAbsolute);
|
||||
string baseUriAbsolute,
|
||||
ClaimsPrincipal user);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Claims;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -109,6 +110,16 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
});
|
||||
}
|
||||
|
||||
public void SetCircuitUser(ClaimsPrincipal user)
|
||||
{
|
||||
var authenticationStateProvider = Services.GetService<AuthenticationStateProvider>() as IHostEnvironmentAuthenticationStateProvider;
|
||||
if (authenticationStateProvider != null)
|
||||
{
|
||||
var authenticationState = new AuthenticationState(user);
|
||||
authenticationStateProvider.SetAuthenticationState(Task.FromResult(authenticationState));
|
||||
}
|
||||
}
|
||||
|
||||
internal void InitializeCircuitAfterPrerender(UnhandledExceptionEventHandler unhandledException)
|
||||
{
|
||||
if (!_initialized)
|
||||
|
|
|
|||
|
|
@ -132,7 +132,8 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
context,
|
||||
client: new CircuitClientProxy(), // This creates an "offline" client.
|
||||
GetFullUri(context.Request),
|
||||
GetFullBaseUri(context.Request));
|
||||
GetFullBaseUri(context.Request),
|
||||
context.User);
|
||||
|
||||
result.UnhandledException += CircuitHost_UnhandledException;
|
||||
context.Response.OnCompleted(() =>
|
||||
|
|
|
|||
|
|
@ -4,17 +4,16 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Text.Encodings.Web;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using Microsoft.AspNetCore.Components.Web.Rendering;
|
||||
using Microsoft.AspNetCore.Components.Rendering;
|
||||
using Microsoft.AspNetCore.Components.Routing;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.JSInterop;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNetCore.Components.Server.Circuits
|
||||
{
|
||||
|
|
@ -40,7 +39,8 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
HttpContext httpContext,
|
||||
CircuitClientProxy client,
|
||||
string uriAbsolute,
|
||||
string baseUriAbsolute)
|
||||
string baseUriAbsolute,
|
||||
ClaimsPrincipal user)
|
||||
{
|
||||
var components = ResolveComponentMetadata(httpContext, client);
|
||||
|
||||
|
|
@ -51,13 +51,6 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
jsRuntime.Initialize(client);
|
||||
componentContext.Initialize(client);
|
||||
|
||||
var authenticationStateProvider = scope.ServiceProvider.GetService<AuthenticationStateProvider>() as IHostEnvironmentAuthenticationStateProvider;
|
||||
if (authenticationStateProvider != null)
|
||||
{
|
||||
var authenticationState = new AuthenticationState(httpContext.User); // TODO: Get this from the hub connection context instead
|
||||
authenticationStateProvider.SetAuthenticationState(Task.FromResult(authenticationState));
|
||||
}
|
||||
|
||||
var uriHelper = (RemoteUriHelper)scope.ServiceProvider.GetRequiredService<IUriHelper>();
|
||||
var navigationInterception = (RemoteNavigationInterception)scope.ServiceProvider.GetRequiredService<INavigationInterception>();
|
||||
if (client.Connected)
|
||||
|
|
@ -102,6 +95,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
|
||||
// Initialize per - circuit data that services need
|
||||
(circuitHost.Services.GetRequiredService<ICircuitAccessor>() as DefaultCircuitAccessor).Circuit = circuitHost.Circuit;
|
||||
circuitHost.SetCircuitUser(user);
|
||||
|
||||
return circuitHost;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,7 +97,8 @@ namespace Microsoft.AspNetCore.Components.Server
|
|||
Context.GetHttpContext(),
|
||||
circuitClient,
|
||||
uriAbsolute,
|
||||
baseUriAbsolute);
|
||||
baseUriAbsolute,
|
||||
Context.User);
|
||||
|
||||
circuitHost.UnhandledException += CircuitHost_UnhandledException;
|
||||
|
||||
|
|
@ -125,6 +126,7 @@ namespace Microsoft.AspNetCore.Components.Server
|
|||
CircuitHost = circuitHost;
|
||||
|
||||
circuitHost.InitializeCircuitAfterPrerender(CircuitHost_UnhandledException);
|
||||
circuitHost.SetCircuitUser(Context.User);
|
||||
circuitHost.SendPendingBatches();
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Claims;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -190,7 +191,7 @@ namespace Microsoft.AspNetCore.Components.Server.Tests.Circuits
|
|||
_circuitIdFactory = circuitIdFactory ?? (() => Guid.NewGuid().ToString());
|
||||
}
|
||||
|
||||
public override CircuitHost CreateCircuitHost(HttpContext httpContext, CircuitClientProxy client, string uriAbsolute, string baseUriAbsolute)
|
||||
public override CircuitHost CreateCircuitHost(HttpContext httpContext, CircuitClientProxy client, string uriAbsolute, string baseUriAbsolute, ClaimsPrincipal user)
|
||||
{
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddScoped<IUriHelper>(_ =>
|
||||
|
|
@ -209,7 +210,7 @@ namespace Microsoft.AspNetCore.Components.Server.Tests.Circuits
|
|||
public Mock<IServiceScope> MockServiceScope { get; }
|
||||
= new Mock<IServiceScope>();
|
||||
|
||||
public override CircuitHost CreateCircuitHost(HttpContext httpContext, CircuitClientProxy client, string uriAbsolute, string baseUriAbsolute)
|
||||
public override CircuitHost CreateCircuitHost(HttpContext httpContext, CircuitClientProxy client, string uriAbsolute, string baseUriAbsolute, ClaimsPrincipal user)
|
||||
{
|
||||
return TestCircuitHost.Create(Guid.NewGuid().ToString(), MockServiceScope.Object);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using BasicTestApp;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
|
||||
using Microsoft.AspNetCore.E2ETesting;
|
||||
using OpenQA.Selenium;
|
||||
using OpenQA.Selenium.Support.UI;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.AspNetCore.Components.E2ETest.Infrastructure
|
||||
|
|
@ -49,5 +49,32 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Infrastructure
|
|||
.Until(driver => (result = driver.FindElement(findBy)) != null);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected void SignInAs(string usernameOrNull, string rolesOrNull, bool useSeparateTab = false)
|
||||
{
|
||||
const string authenticationPageUrl = "/Authentication";
|
||||
var baseRelativeUri = usernameOrNull == null
|
||||
? $"{authenticationPageUrl}?signout=true"
|
||||
: $"{authenticationPageUrl}?username={usernameOrNull}&roles={rolesOrNull}";
|
||||
|
||||
if (useSeparateTab)
|
||||
{
|
||||
// Some tests need to change the authentication state without discarding the
|
||||
// original page, but this adds several seconds of delay
|
||||
var javascript = (IJavaScriptExecutor)Browser;
|
||||
var originalWindow = Browser.CurrentWindowHandle;
|
||||
javascript.ExecuteScript("window.open()");
|
||||
Browser.SwitchTo().Window(Browser.WindowHandles.Last());
|
||||
Navigate(baseRelativeUri);
|
||||
WaitUntilExists(By.CssSelector("h1#authentication"));
|
||||
javascript.ExecuteScript("window.close()");
|
||||
Browser.SwitchTo().Window(originalWindow);
|
||||
}
|
||||
else
|
||||
{
|
||||
Navigate(baseRelativeUri);
|
||||
WaitUntilExists(By.CssSelector("h1#authentication"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using BasicTestApp;
|
||||
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure;
|
||||
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
|
||||
using Microsoft.AspNetCore.E2ETesting;
|
||||
|
|
@ -14,16 +15,15 @@ using Xunit.Abstractions;
|
|||
|
||||
namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
||||
{
|
||||
public class PrerenderingTest : ServerTestBase<AspNetSiteServerFixture>
|
||||
[Collection("auth")] // Because auth uses cookies, this can't run in parallel with other auth tests
|
||||
public class PrerenderingTest : BasicTestAppTestBase
|
||||
{
|
||||
public PrerenderingTest(
|
||||
BrowserFixture browserFixture,
|
||||
AspNetSiteServerFixture serverFixture,
|
||||
ToggleExecutionModeServerFixture<Program> serverFixture,
|
||||
ITestOutputHelper output)
|
||||
: base(browserFixture, serverFixture, output)
|
||||
: base(browserFixture, serverFixture.WithServerExecution(), output)
|
||||
{
|
||||
_serverFixture.Environment = AspNetEnvironment.Development;
|
||||
_serverFixture.BuildWebHostMethod = TestServer.Program.BuildWebHost;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -97,6 +97,24 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
|||
Assert.Equal(expectedUri, response.Headers.Location);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, null)]
|
||||
[InlineData(null, "Bert")]
|
||||
[InlineData("Bert", null)]
|
||||
[InlineData("Bert", "Treb")]
|
||||
public void CanAccessAuthenticationStateDuringStaticPrerendering(string initialUsername, string interactiveUsername)
|
||||
{
|
||||
// See that the authentication state is usable during the initial prerendering
|
||||
SignInAs(initialUsername, null);
|
||||
Navigate("/prerendered/prerendered-transition");
|
||||
Browser.Equal($"Hello, {initialUsername ?? "anonymous"}!", () => Browser.FindElement(By.TagName("h1")).Text);
|
||||
|
||||
// See that during connection, we update to whatever the latest authentication state now is
|
||||
SignInAs(interactiveUsername, null, useSeparateTab: true);
|
||||
BeginInteractivity();
|
||||
Browser.Equal($"Hello, {interactiveUsername ?? "anonymous"}!", () => Browser.FindElement(By.TagName("h1")).Text);
|
||||
}
|
||||
|
||||
private void BeginInteractivity()
|
||||
{
|
||||
Browser.FindElement(By.Id("load-boot-script")).Click();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using BasicTestApp;
|
||||
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
|
||||
using Microsoft.AspNetCore.Components.E2ETest.Tests;
|
||||
using Microsoft.AspNetCore.E2ETesting;
|
||||
using OpenQA.Selenium;
|
||||
using OpenQA.Selenium.Support.UI;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
||||
{
|
||||
public class ServerAuthTest : AuthTest
|
||||
{
|
||||
public ServerAuthTest(BrowserFixture browserFixture, ToggleExecutionModeServerFixture<Program> serverFixture, ITestOutputHelper output)
|
||||
: base(browserFixture, serverFixture.WithServerExecution(), output)
|
||||
{
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, null)]
|
||||
[InlineData(null, "Someone")]
|
||||
[InlineData("Someone", null)]
|
||||
[InlineData("Someone", "Someone")]
|
||||
public void UpdatesAuthenticationStateWhenReconnecting(
|
||||
string usernameBefore, string usernameAfter)
|
||||
{
|
||||
// Establish state before disconnection
|
||||
SignInAs(usernameBefore, usernameBefore == null ? null : "TestRole");
|
||||
var appElement = MountAndNavigateToAuthTest(AuthorizeViewCases);
|
||||
AssertState(usernameBefore);
|
||||
|
||||
// Change authentication state and force reconnection
|
||||
SignInAs(usernameAfter, usernameAfter == null ? null : "TestRole", useSeparateTab: true);
|
||||
PerformReconnection();
|
||||
AssertState(usernameAfter);
|
||||
|
||||
void AssertState(string username)
|
||||
{
|
||||
if (username == null)
|
||||
{
|
||||
Browser.Equal("You're not authorized, anonymous", () =>
|
||||
appElement.FindElement(By.CssSelector("#authorize-role .not-authorized")).Text);
|
||||
}
|
||||
else
|
||||
{
|
||||
Browser.Equal($"Welcome, {username}!", () =>
|
||||
appElement.FindElement(By.CssSelector("#authorize-role .authorized")).Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void PerformReconnection()
|
||||
{
|
||||
((IJavaScriptExecutor)Browser).ExecuteScript("Blazor._internal.forceCloseConnection()");
|
||||
|
||||
// Wait until the reconnection dialog has been shown but is now hidden
|
||||
new WebDriverWait(Browser, TimeSpan.FromSeconds(10))
|
||||
.Until(driver => driver.FindElement(By.Id("components-reconnect-modal"))?.GetCssValue("display") == "none");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -83,12 +83,4 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
|||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class ServerAuthTest : AuthTest
|
||||
{
|
||||
public ServerAuthTest(BrowserFixture browserFixture, ToggleExecutionModeServerFixture<Program> serverFixture, ITestOutputHelper output)
|
||||
: base(browserFixture, serverFixture.WithServerExecution(), output)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,15 +11,16 @@ using Xunit.Abstractions;
|
|||
|
||||
namespace Microsoft.AspNetCore.Components.E2ETest.Tests
|
||||
{
|
||||
[Collection("auth")] // Because auth uses cookies, this can't run in parallel with other auth tests
|
||||
public class AuthTest : BasicTestAppTestBase
|
||||
{
|
||||
// These strings correspond to the links in BasicTestApp\AuthTest\Links.razor
|
||||
const string CascadingAuthenticationStateLink = "Cascading authentication state";
|
||||
const string AuthorizeViewCases = "AuthorizeView cases";
|
||||
const string PageAllowingAnonymous = "Page allowing anonymous";
|
||||
const string PageRequiringAuthorization = "Page requiring any authentication";
|
||||
const string PageRequiringPolicy = "Page requiring policy";
|
||||
const string PageRequiringRole = "Page requiring role";
|
||||
protected const string CascadingAuthenticationStateLink = "Cascading authentication state";
|
||||
protected const string AuthorizeViewCases = "AuthorizeView cases";
|
||||
protected const string PageAllowingAnonymous = "Page allowing anonymous";
|
||||
protected const string PageRequiringAuthorization = "Page requiring any authentication";
|
||||
protected const string PageRequiringPolicy = "Page requiring policy";
|
||||
protected const string PageRequiringRole = "Page requiring role";
|
||||
|
||||
public AuthTest(
|
||||
BrowserFixture browserFixture,
|
||||
|
|
@ -184,7 +185,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
|
|||
appElement.FindElement(By.CssSelector("#auth-failure")).Text);
|
||||
}
|
||||
|
||||
IWebElement MountAndNavigateToAuthTest(string authLinkText)
|
||||
protected IWebElement MountAndNavigateToAuthTest(string authLinkText)
|
||||
{
|
||||
Navigate(ServerPathBase);
|
||||
var appElement = MountTestComponent<BasicTestApp.AuthTest.AuthRouter>();
|
||||
|
|
@ -192,15 +193,5 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
|
|||
appElement.FindElement(By.LinkText(authLinkText)).Click();
|
||||
return appElement;
|
||||
}
|
||||
|
||||
void SignInAs(string usernameOrNull, string rolesOrNull)
|
||||
{
|
||||
const string authenticationPageUrl = "/Authentication";
|
||||
var baseRelativeUri = usernameOrNull == null
|
||||
? $"{authenticationPageUrl}?signout=true"
|
||||
: $"{authenticationPageUrl}?username={usernameOrNull}&roles={rolesOrNull}";
|
||||
Navigate(baseRelativeUri);
|
||||
WaitUntilExists(By.CssSelector("h1#authentication"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,10 @@
|
|||
@inject IComponentContext ComponentContext
|
||||
|
||||
<CascadingAuthenticationState>
|
||||
<h1>Hello</h1>
|
||||
<AuthorizeView>
|
||||
<Authorized><h1>Hello, @context.User.Identity.Name!</h1></Authorized>
|
||||
<NotAuthorized><h1>Hello, anonymous!</h1></NotAuthorized>
|
||||
</AuthorizeView>
|
||||
|
||||
<p>
|
||||
Current state:
|
||||
|
|
|
|||
Loading…
Reference in New Issue