Skip to content

Components auth step 2 #10293

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
May 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions eng/GenAPI.exclusions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ T:Microsoft.AspNetCore.Components.RenderTree.RenderTreeFrame
T:Microsoft.AspNetCore.Mvc.ApplicationModels.PageParameterModel
T:Microsoft.AspNetCore.Mvc.ApplicationModels.PagePropertyModel
# Manually implemented - https://github.com/aspnet/AspNetCore/issues/8825
T:Microsoft.AspNetCore.Components.AuthorizeView
T:Microsoft.AspNetCore.Components.CascadingAuthenticationState
T:Microsoft.AspNetCore.Components.CascadingValue`1
T:Microsoft.AspNetCore.Components.Forms.DataAnnotationsValidator
T:Microsoft.AspNetCore.Components.Forms.EditForm
Expand Down
3 changes: 3 additions & 0 deletions src/Components/Blazor/Blazor/test/WebAssemblyUriHelperTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ public void ComputesCorrectBaseUri(string baseUri, string expectedResult)
[InlineData("scheme://host/path/", "scheme://host/path/", "")]
[InlineData("scheme://host/path/", "scheme://host/path/more", "more")]
[InlineData("scheme://host/path/", "scheme://host/path", "")]
[InlineData("scheme://host/path/", "scheme://host/path#hash", "#hash")]
[InlineData("scheme://host/path/", "scheme://host/path/#hash", "#hash")]
[InlineData("scheme://host/path/", "scheme://host/path/more#hash", "more#hash")]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Support for these cases was needed for the E2E tests. It's because we use a #server suffix to trigger server-side execution (and always have done - that's not new), and it turns out the ToBaseRelativePath logic didn't understand the case that's now on line 32 here.

public void ComputesCorrectValidBaseRelativePaths(string baseUri, string absoluteUri, string expectedResult)
{
var actualResult = _uriHelper.ToBaseRelativePath(baseUri, absoluteUri);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,32 @@ public readonly partial struct RenderTreeFrame
// Built-in components: https://github.com/aspnet/AspNetCore/issues/8825
namespace Microsoft.AspNetCore.Components
{
public partial class AuthorizeView : Microsoft.AspNetCore.Components.ComponentBase
{
public AuthorizeView() { }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public Microsoft.AspNetCore.Components.RenderFragment<Microsoft.AspNetCore.Components.AuthenticationState> Authorized { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; } }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public Microsoft.AspNetCore.Components.RenderFragment Authorizing { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; } }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public Microsoft.AspNetCore.Components.RenderFragment<Microsoft.AspNetCore.Components.AuthenticationState> ChildContent { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; } }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public Microsoft.AspNetCore.Components.RenderFragment NotAuthorized { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; } }
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder) { }
[System.Diagnostics.DebuggerStepThroughAttribute]
protected override System.Threading.Tasks.Task OnParametersSetAsync() { throw null; }
}

public partial class CascadingAuthenticationState : Microsoft.AspNetCore.Components.ComponentBase, System.IDisposable
{
public CascadingAuthenticationState() { }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public Microsoft.AspNetCore.Components.RenderFragment ChildContent { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; } }
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder) { }
protected override void OnInit() { }
void System.IDisposable.Dispose() { }
}

public partial class CascadingValue<T> : Microsoft.AspNetCore.Components.IComponent
{
public CascadingValue() { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,6 @@ public event Microsoft.AspNetCore.Components.AuthenticationStateChangedHandler A
public abstract System.Threading.Tasks.Task<Microsoft.AspNetCore.Components.AuthenticationState> GetAuthenticationStateAsync();
protected void NotifyAuthenticationStateChanged(System.Threading.Tasks.Task<Microsoft.AspNetCore.Components.AuthenticationState> task) { }
}
public partial class AuthorizeView : Microsoft.AspNetCore.Components.ComponentBase
{
public AuthorizeView() { }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public Microsoft.AspNetCore.Components.RenderFragment<Microsoft.AspNetCore.Components.AuthenticationState> Authorized { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public Microsoft.AspNetCore.Components.RenderFragment Authorizing { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public Microsoft.AspNetCore.Components.RenderFragment<Microsoft.AspNetCore.Components.AuthenticationState> ChildContent { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public Microsoft.AspNetCore.Components.RenderFragment NotAuthorized { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder) { }
[System.Diagnostics.DebuggerStepThroughAttribute]
protected override System.Threading.Tasks.Task OnParametersSetAsync() { throw null; }
}
[Microsoft.AspNetCore.Components.BindElementAttribute("select", null, "value", "onchange")]
[Microsoft.AspNetCore.Components.BindElementAttribute("textarea", null, "value", "onchange")]
[Microsoft.AspNetCore.Components.BindInputElementAttribute("checkbox", null, "checked", "onchange")]
Expand Down Expand Up @@ -85,15 +70,6 @@ public static partial class BindMethods
public static System.Action<Microsoft.AspNetCore.Components.UIEventArgs> SetValueHandler(System.Action<string> setter, string existingValue) { throw null; }
public static System.Action<Microsoft.AspNetCore.Components.UIEventArgs> SetValueHandler<T>(System.Action<T> setter, T existingValue) { throw null; }
}
public partial class CascadingAuthenticationState : Microsoft.AspNetCore.Components.ComponentBase, System.IDisposable
{
public CascadingAuthenticationState() { }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public Microsoft.AspNetCore.Components.RenderFragment ChildContent { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder) { }
protected override void OnInit() { }
void System.IDisposable.Dispose() { }
}
[System.AttributeUsageAttribute(System.AttributeTargets.Property, AllowMultiple=false, Inherited=false)]
public sealed partial class CascadingParameterAttribute : System.Attribute
{
Expand Down
10 changes: 5 additions & 5 deletions src/Components/Components/src/Auth/AuthenticationState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,6 @@ namespace Microsoft.AspNetCore.Components
/// </summary>
public class AuthenticationState
{
/// <summary>
/// Gets a <see cref="ClaimsPrincipal"/> that describes the current user.
/// </summary>
public ClaimsPrincipal User { get; }

/// <summary>
/// Constructs an instance of <see cref="AuthenticationState"/>.
/// </summary>
Expand All @@ -24,5 +19,10 @@ public AuthenticationState(ClaimsPrincipal user)
{
User = user ?? throw new ArgumentNullException(nameof(user));
}

/// <summary>
/// Gets a <see cref="ClaimsPrincipal"/> that describes the current user.
/// </summary>
public ClaimsPrincipal User { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,16 @@ namespace Microsoft.AspNetCore.Components
public abstract class AuthenticationStateProvider
{
/// <summary>
/// Gets an <see cref="AuthenticationState"/> instance that describes
/// the current user.
/// Asynchronously gets an <see cref="AuthenticationState"/> that describes the current user.
/// </summary>
/// <returns>An <see cref="AuthenticationState"/> instance that describes the current user.</returns>
/// <returns>A task that, when resolved, gives an <see cref="AuthenticationState"/> instance that describes the current user.</returns>
public abstract Task<AuthenticationState> GetAuthenticationStateAsync();

/// <summary>
/// An event that provides notification when the <see cref="AuthenticationState"/>
/// has changed. For example, this event may be raised if a user logs in or out.
/// </summary>
#pragma warning disable 0067 // "Never used" (it's only raised by subclasses)
public event AuthenticationStateChangedHandler AuthenticationStateChanged;
#pragma warning restore 0067

/// <summary>
/// Raises the <see cref="AuthenticationStateChanged"/> event.
Expand Down
7 changes: 5 additions & 2 deletions src/Components/Components/src/UriHelperBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,15 +154,18 @@ public string ToBaseRelativePath(string baseUri, string locationAbsolute)
// baseUri ends with a slash), and from that we return "something"
return locationAbsolute.Substring(baseUri.Length);
}
else if ($"{locationAbsolute}/".Equals(baseUri, StringComparison.Ordinal))

var hashIndex = locationAbsolute.IndexOf('#');
var locationAbsoluteNoHash = hashIndex < 0 ? locationAbsolute : locationAbsolute.Substring(0, hashIndex);
if ($"{locationAbsoluteNoHash}/".Equals(baseUri, StringComparison.Ordinal))
{
// Special case: for the base URI "/something/", if you're at
// "/something" then treat it as if you were at "/something/" (i.e.,
// with the trailing slash). It's a bit ambiguous because we don't know
// whether the server would return the same page whether or not the
// slash is present, but ASP.NET Core at least does by default when
// using PathBase.
return string.Empty;
return locationAbsolute.Substring(baseUri.Length - 1);
}

var message = $"The URI '{locationAbsolute}' is not contained by the base URI '{baseUri}'.";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Infrastructure
public class BasicTestAppTestBase : ServerTestBase<ToggleExecutionModeServerFixture<Program>>
{
public string ServerPathBase
=> "/subdir" + (_serverFixture.UsingAspNetHost ? "#server" : "");
=> "/subdir" + (_serverFixture.ExecutionMode == ExecutionMode.Server ? "#server" : "");

public BasicTestAppTestBase(
BrowserFixture browserFixture,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ public class ToggleExecutionModeServerFixture<TClientProgram>
: ServerFixture
{
public string PathBase { get; set; }
public bool UsingAspNetHost { get; private set; }

public ExecutionMode ExecutionMode { get; set; } = ExecutionMode.Client;

private AspNetSiteServerFixture.BuildWebHost _buildWebHostMethod;
private IDisposable _serverToDispose;
Expand All @@ -18,7 +19,6 @@ public void UseAspNetHost(AspNetSiteServerFixture.BuildWebHost buildWebHostMetho
{
_buildWebHostMethod = buildWebHostMethod
?? throw new ArgumentNullException(nameof(buildWebHostMethod));
UsingAspNetHost = true;
}

protected override string StartAndGetRootUri()
Expand Down Expand Up @@ -46,4 +46,6 @@ public override void Dispose()
_serverToDispose?.Dispose();
}
}

public enum ExecutionMode { Client, Server }
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
using Xunit;
using Xunit.Abstractions;

namespace Microsoft.AspNetCore.Components.E2ETests.ServerExecutionTests
namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
{
public class PrerenderingTest : ServerTestBase<AspNetSiteServerFixture>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ internal static class ServerExecutionTestExtensions
public static ToggleExecutionModeServerFixture<T> WithServerExecution<T>(this ToggleExecutionModeServerFixture<T> serverFixture)
{
serverFixture.UseAspNetHost(TestServer.Program.BuildWebHost);
serverFixture.ExecutionMode = ExecutionMode.Server;
return serverFixture;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,12 @@ public ServerKeyTest(BrowserFixture browserFixture, ToggleExecutionModeServerFix
{
}
}

public class ServerAuthTest : AuthTest
{
public ServerAuthTest(BrowserFixture browserFixture, ToggleExecutionModeServerFixture<Program> serverFixture, ITestOutputHelper output)
: base(browserFixture, serverFixture.WithServerExecution(), output)
{
}
}
}
92 changes: 92 additions & 0 deletions src/Components/test/E2ETest/Tests/AuthTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// 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 BasicTestApp;
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure;
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
using Microsoft.AspNetCore.E2ETesting;
using OpenQA.Selenium;
using Xunit;
using Xunit.Abstractions;

namespace Microsoft.AspNetCore.Components.E2ETest.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";

public AuthTest(
BrowserFixture browserFixture,
ToggleExecutionModeServerFixture<Program> serverFixture,
ITestOutputHelper output)
: base(browserFixture, serverFixture, output)
{
// Normally, the E2E tests use the Blazor dev server if they are testing
// client-side execution. But for the auth tests, we always have to run
// in "hosted on ASP.NET Core" mode, because we get the auth state from it.
serverFixture.UseAspNetHost(TestServer.Program.BuildWebHost);
}

[Fact]
public void CascadingAuthenticationState_Unauthenticated()
{
SignInAs(null);

var appElement = MountAndNavigateToAuthTest(CascadingAuthenticationStateLink);

Browser.Equal("False", () => appElement.FindElement(By.Id("identity-authenticated")).Text);
Browser.Equal(string.Empty, () => appElement.FindElement(By.Id("identity-name")).Text);
Browser.Equal("(none)", () => appElement.FindElement(By.Id("test-claim")).Text);
}

[Fact]
public void CascadingAuthenticationState_Authenticated()
{
SignInAs("someone cool");

var appElement = MountAndNavigateToAuthTest(CascadingAuthenticationStateLink);

Browser.Equal("True", () => appElement.FindElement(By.Id("identity-authenticated")).Text);
Browser.Equal("someone cool", () => appElement.FindElement(By.Id("identity-name")).Text);
Browser.Equal("Test claim value", () => appElement.FindElement(By.Id("test-claim")).Text);
}

[Fact]
public void AuthorizeViewCases_NoAuthorizationRule_Unauthenticated()
{
SignInAs(null);
MountAndNavigateToAuthTest(AuthorizeViewCases);
WaitUntilExists(By.CssSelector("#no-authorization-rule .not-authorized"));
}

[Fact]
public void AuthorizeViewCases_NoAuthorizationRule_Authenticated()
{
SignInAs("Some User");
var appElement = MountAndNavigateToAuthTest(AuthorizeViewCases);
Browser.Equal("Welcome, Some User!", () =>
appElement.FindElement(By.CssSelector("#no-authorization-rule .authorized")).Text);
}

IWebElement MountAndNavigateToAuthTest(string authLinkText)
{
Navigate(ServerPathBase);
var appElement = MountTestComponent<BasicTestApp.AuthTest.AuthRouter>();
WaitUntilExists(By.Id("auth-links"));
appElement.FindElement(By.LinkText(authLinkText)).Click();
return appElement;
}

void SignInAs(string usernameOrNull)
{
const string authenticationPageUrl = "/Authentication";
var baseRelativeUri = usernameOrNull == null
? $"{authenticationPageUrl}?signout=true"
: $"{authenticationPageUrl}?username={usernameOrNull}";
Navigate(baseRelativeUri);
WaitUntilExists(By.CssSelector("h1#authentication"));
}
}
}
2 changes: 1 addition & 1 deletion src/Components/test/E2ETest/Tests/BindTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public BindTest(
protected override void InitializeAsyncCore()
{
// On WebAssembly, page reloads are expensive so skip if possible
Navigate(ServerPathBase, noReload: !_serverFixture.UsingAspNetHost);
Navigate(ServerPathBase, noReload: _serverFixture.ExecutionMode == ExecutionMode.Client);
MountTestComponent<BindCasesComponent>();
WaitUntilExists(By.Id("bind-cases"));
}
Expand Down
2 changes: 1 addition & 1 deletion src/Components/test/E2ETest/Tests/CascadingValueTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public CascadingValueTest(

protected override void InitializeAsyncCore()
{
Navigate(ServerPathBase, noReload: !_serverFixture.UsingAspNetHost);
Navigate(ServerPathBase, noReload: _serverFixture.ExecutionMode == ExecutionMode.Client);
MountTestComponent<BasicTestApp.CascadingValueTest.CascadingValueSupplier>();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public ComponentRenderingTest(

protected override void InitializeAsyncCore()
{
Navigate(ServerPathBase, noReload: !_serverFixture.UsingAspNetHost);
Navigate(ServerPathBase, noReload: _serverFixture.ExecutionMode == ExecutionMode.Client);
}

[Fact]
Expand Down
2 changes: 1 addition & 1 deletion src/Components/test/E2ETest/Tests/EventBubblingTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public EventBubblingTest(

protected override void InitializeAsyncCore()
{
Navigate(ServerPathBase, noReload: !_serverFixture.UsingAspNetHost);
Navigate(ServerPathBase, noReload: _serverFixture.ExecutionMode == ExecutionMode.Client);
MountTestComponent<EventBubblingComponent>();
WaitUntilExists(By.Id("event-bubbling"));
}
Expand Down
2 changes: 1 addition & 1 deletion src/Components/test/E2ETest/Tests/EventCallbackTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public EventCallbackTest(
protected override void InitializeAsyncCore()
{
// On WebAssembly, page reloads are expensive so skip if possible
Navigate(ServerPathBase, noReload: !_serverFixture.UsingAspNetHost);
Navigate(ServerPathBase, noReload: _serverFixture.ExecutionMode == ExecutionMode.Client);
MountTestComponent<BasicTestApp.EventCallbackTest.EventCallbackCases>();
}

Expand Down
2 changes: 1 addition & 1 deletion src/Components/test/E2ETest/Tests/FormsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public FormsTest(
protected override void InitializeAsyncCore()
{
// On WebAssembly, page reloads are expensive so skip if possible
Navigate(ServerPathBase, noReload: !_serverFixture.UsingAspNetHost);
Navigate(ServerPathBase, noReload: _serverFixture.ExecutionMode == ExecutionMode.Client);
}

[Fact]
Expand Down
2 changes: 1 addition & 1 deletion src/Components/test/E2ETest/Tests/InteropTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public void CanInvokeDotNetMethods()

// Include the sync assertions only when running under WebAssembly
var expectedValues = expectedAsyncValues;
if (!_serverFixture.UsingAspNetHost)
if (_serverFixture.ExecutionMode == ExecutionMode.Client)
{
foreach (var kvp in expectedSyncValues)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Components/test/E2ETest/Tests/KeyTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public KeyTest(
protected override void InitializeAsyncCore()
{
// On WebAssembly, page reloads are expensive so skip if possible
Navigate(ServerPathBase, noReload: !_serverFixture.UsingAspNetHost);
Navigate(ServerPathBase, noReload: _serverFixture.ExecutionMode == ExecutionMode.Client);
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@page "/AuthHome"

Select an auth test below.
Loading