From 0c82d94a544caa7f39f1692c46d8d16a833daaa8 Mon Sep 17 00:00:00 2001 From: Chris Ross Date: Fri, 11 Aug 2017 11:50:25 -0700 Subject: [PATCH] #772 Signout FormPost test (#1358) --- .../OpenIdConnect/OpenIdConnectTests.cs | 36 +++++--- .../OpenIdConnect/TestSettings.cs | 90 ++++++++++++++----- 2 files changed, 91 insertions(+), 35 deletions(-) diff --git a/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/OpenIdConnectTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/OpenIdConnectTests.cs index b7ac1f82d5..32be26d33e 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/OpenIdConnectTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/OpenIdConnectTests.cs @@ -182,27 +182,39 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect } [Fact] - public async Task SignOutWithDefaultRedirectUri() + public async Task SignOutFormPostWithDefaultRedirectUri() { - var configuration = TestServerBuilder.CreateDefaultOpenIdConnectConfiguration(); - var server = TestServerBuilder.CreateServer(o => + var settings = new TestSettings(o => { + o.AuthenticationMethod = OpenIdConnectRedirectBehavior.FormPost; o.Authority = TestServerBuilder.DefaultAuthority; o.ClientId = "Test Id"; - o.Configuration = configuration; }); + var server = settings.CreateTestServer(); + + var transaction = await server.SendAsync(DefaultHost + TestServerBuilder.Signout); + Assert.Equal(HttpStatusCode.OK, transaction.Response.StatusCode); + + settings.ValidateSignoutFormPost(transaction, + OpenIdConnectParameterNames.PostLogoutRedirectUri); + } + + [Fact] + public async Task SignOutRedirectWithDefaultRedirectUri() + { + var settings = new TestSettings(o => + { + o.AuthenticationMethod = OpenIdConnectRedirectBehavior.RedirectGet; + o.Authority = TestServerBuilder.DefaultAuthority; + o.ClientId = "Test Id"; + }); + var server = settings.CreateTestServer(); var transaction = await server.SendAsync(DefaultHost + TestServerBuilder.Signout); Assert.Equal(HttpStatusCode.Redirect, transaction.Response.StatusCode); - Assert.True(transaction.Response.Headers.Location.AbsoluteUri.StartsWith(configuration.EndSessionEndpoint)); - var query = transaction.Response.Headers.Location.Query.Substring(1).Split('&') - .Select(each => each.Split('=')) - .ToDictionary(pair => pair[0], pair => pair[1]); - - string redirectUri; - Assert.True(query.TryGetValue("post_logout_redirect_uri", out redirectUri)); - Assert.Equal(UrlEncoder.Default.Encode("https://example.com/signout-callback-oidc"), redirectUri, true); + settings.ValidateSignoutRedirect(transaction.Response.Headers.Location, + OpenIdConnectParameterNames.PostLogoutRedirectUri); } [Fact] diff --git a/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/TestSettings.cs b/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/TestSettings.cs index 9d9e5537fe..bf9df40384 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/TestSettings.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/TestSettings.cs @@ -80,6 +80,44 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect return formInputs; } + public IDictionary ValidateSignoutFormPost(TestTransaction transaction, params string[] parametersToValidate) + { + IDictionary formInputs = null; + var errors = new List(); + var xdoc = XDocument.Parse(transaction.ResponseText.Replace("doctype", "DOCTYPE")); + var forms = xdoc.Descendants("form"); + if (forms.Count() != 1) + { + errors.Add("Only one form element is expected in response body."); + } + else + { + formInputs = forms.Single() + .Elements("input") + .ToDictionary(elem => elem.Attribute("name").Value, + elem => elem.Attribute("value").Value); + + ValidateParameters(formInputs, parametersToValidate, errors, htmlEncoded: false); + } + + if (errors.Any()) + { + var buf = new StringBuilder(); + buf.AppendLine($"The signout form post is not valid."); + // buf.AppendLine(); + + foreach (var error in errors) + { + buf.AppendLine(error); + } + + Debug.WriteLine(buf.ToString()); + Assert.True(false, buf.ToString()); + } + + return formInputs; + } + public IDictionary ValidateChallengeRedirect(Uri redirectUri, params string[] parametersToValidate) => ValidateRedirectCore(redirectUri, OpenIdConnectRequestType.Authentication, parametersToValidate); @@ -156,6 +194,9 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect case OpenIdConnectParameterNames.VersionTelemetry: ValidateVersionTelemetry(actualValues, errors, htmlEncoded); break; + case OpenIdConnectParameterNames.PostLogoutRedirectUri: + ValidatePostLogoutRedirectUri(actualValues, errors, htmlEncoded); + break; default: throw new InvalidOperationException($"Unknown parameter \"{paramToValidate}\"."); } @@ -186,42 +227,45 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect } } - private void ValidateClientId(IDictionary actualQuery, ICollection errors, bool htmlEncoded) => - ValidateQueryParameter(OpenIdConnectParameterNames.ClientId, _options.ClientId, actualQuery, errors, htmlEncoded); + private void ValidateClientId(IDictionary actualParams, ICollection errors, bool htmlEncoded) => + ValidateParameter(OpenIdConnectParameterNames.ClientId, _options.ClientId, actualParams, errors, htmlEncoded); - private void ValidateResponseType(IDictionary actualQuery, ICollection errors, bool htmlEncoded) => - ValidateQueryParameter(OpenIdConnectParameterNames.ResponseType, _options.ResponseType, actualQuery, errors, htmlEncoded); + private void ValidateResponseType(IDictionary actualParams, ICollection errors, bool htmlEncoded) => + ValidateParameter(OpenIdConnectParameterNames.ResponseType, _options.ResponseType, actualParams, errors, htmlEncoded); - private void ValidateResponseMode(IDictionary actualQuery, ICollection errors, bool htmlEncoded) => - ValidateQueryParameter(OpenIdConnectParameterNames.ResponseMode, _options.ResponseMode, actualQuery, errors, htmlEncoded); + private void ValidateResponseMode(IDictionary actualParams, ICollection errors, bool htmlEncoded) => + ValidateParameter(OpenIdConnectParameterNames.ResponseMode, _options.ResponseMode, actualParams, errors, htmlEncoded); - private void ValidateScope(IDictionary actualQuery, ICollection errors, bool htmlEncoded) => - ValidateQueryParameter(OpenIdConnectParameterNames.Scope, string.Join(" ", _options.Scope), actualQuery, errors, htmlEncoded); + private void ValidateScope(IDictionary actualParams, ICollection errors, bool htmlEncoded) => + ValidateParameter(OpenIdConnectParameterNames.Scope, string.Join(" ", _options.Scope), actualParams, errors, htmlEncoded); - private void ValidateRedirectUri(IDictionary actualQuery, ICollection errors, bool htmlEncoded) => - ValidateQueryParameter(OpenIdConnectParameterNames.RedirectUri, TestServerBuilder.TestHost + _options.CallbackPath, actualQuery, errors, htmlEncoded); + private void ValidateRedirectUri(IDictionary actualParams, ICollection errors, bool htmlEncoded) => + ValidateParameter(OpenIdConnectParameterNames.RedirectUri, TestServerBuilder.TestHost + _options.CallbackPath, actualParams, errors, htmlEncoded); - private void ValidateResource(IDictionary actualQuery, ICollection errors, bool htmlEncoded) => - ValidateQueryParameter(OpenIdConnectParameterNames.RedirectUri, _options.Resource, actualQuery, errors, htmlEncoded); + private void ValidateResource(IDictionary actualParams, ICollection errors, bool htmlEncoded) => + ValidateParameter(OpenIdConnectParameterNames.RedirectUri, _options.Resource, actualParams, errors, htmlEncoded); - private void ValidateState(IDictionary actualQuery, ICollection errors, bool htmlEncoded) => - ValidateQueryParameter(OpenIdConnectParameterNames.State, ExpectedState, actualQuery, errors, htmlEncoded); + private void ValidateState(IDictionary actualParams, ICollection errors, bool htmlEncoded) => + ValidateParameter(OpenIdConnectParameterNames.State, ExpectedState, actualParams, errors, htmlEncoded); - private void ValidateSkuTelemetry(IDictionary actualQuery, ICollection errors, bool htmlEncoded) => - ValidateQueryParameter(OpenIdConnectParameterNames.SkuTelemetry, "ID_NET", actualQuery, errors, htmlEncoded); + private void ValidateSkuTelemetry(IDictionary actualParams, ICollection errors, bool htmlEncoded) => + ValidateParameter(OpenIdConnectParameterNames.SkuTelemetry, "ID_NET", actualParams, errors, htmlEncoded); - private void ValidateVersionTelemetry(IDictionary actualQuery, ICollection errors, bool htmlEncoded) => - ValidateQueryParameter(OpenIdConnectParameterNames.VersionTelemetry, typeof(OpenIdConnectMessage).GetTypeInfo().Assembly.GetName().Version.ToString(), actualQuery, errors, htmlEncoded); + private void ValidateVersionTelemetry(IDictionary actualParams, ICollection errors, bool htmlEncoded) => + ValidateParameter(OpenIdConnectParameterNames.VersionTelemetry, typeof(OpenIdConnectMessage).GetTypeInfo().Assembly.GetName().Version.ToString(), actualParams, errors, htmlEncoded); - private void ValidateQueryParameter( + private void ValidatePostLogoutRedirectUri(IDictionary actualParams, ICollection errors, bool htmlEncoded) => + ValidateParameter(OpenIdConnectParameterNames.PostLogoutRedirectUri, "https://example.com/signout-callback-oidc", actualParams, errors, htmlEncoded); + + private void ValidateParameter( string parameterName, string expectedValue, - IDictionary actualQuery, + IDictionary actualParams, ICollection errors, bool htmlEncoded) { string actualValue; - if (actualQuery.TryGetValue(parameterName, out actualValue)) + if (actualParams.TryGetValue(parameterName, out actualValue)) { if (htmlEncoded) { @@ -230,12 +274,12 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect if (actualValue != expectedValue) { - errors.Add($"Query parameter {parameterName}'s expected value is {expectedValue} but its actual value is {actualValue}"); + errors.Add($"Parameter {parameterName}'s expected value is '{expectedValue}' but its actual value is '{actualValue}'"); } } else { - errors.Add($"Query parameter {parameterName} is missing"); + errors.Add($"Parameter {parameterName} is missing"); } } }