From 2c881cf6a697b764cea683cd61739cae4e814da2 Mon Sep 17 00:00:00 2001 From: Praburaj Date: Fri, 6 Feb 2015 12:09:11 -0800 Subject: [PATCH] Adding verification for openIdconnect logout flow As well removed the logic to wait for the creation of the db file in userprofile folder as we now have retry logic. --- .../Controllers/AccountController.cs | 2 +- test/E2ETests/Common/DeploymentUtility.cs | 65 +++++-------------- test/E2ETests/Common/Helpers.cs | 3 +- .../OpenIdConnectLoginScenarios.cs | 19 ++++++ test/E2ETests/Implementation/Scenarios.cs | 4 +- test/E2ETests/NtlmAuthentationTest.cs | 2 +- test/E2ETests/OpenIdConnectTests.cs | 4 +- test/E2ETests/PublishAndRunTests.cs | 2 +- test/E2ETests/SmokeTests.cs | 2 +- .../OpenIdConnectNotifications.cs | 8 +++ 10 files changed, 53 insertions(+), 58 deletions(-) diff --git a/src/MusicStore/Controllers/AccountController.cs b/src/MusicStore/Controllers/AccountController.cs index 903fe4dd7c..4ec5d0d077 100644 --- a/src/MusicStore/Controllers/AccountController.cs +++ b/src/MusicStore/Controllers/AccountController.cs @@ -437,7 +437,7 @@ namespace MusicStore.Controllers // TODO: Currently SignInManager.SignOut does not sign out OpenIdc and does not have a way to pass in a specific // AuthType to sign out. var appEnv = Context.RequestServices.GetService(); - if (appEnv.EnvironmentName == "OpenIdConnect") + if (appEnv.EnvironmentName.StartsWith("OpenIdConnect")) { Response.SignOut("OpenIdConnect"); } diff --git a/test/E2ETests/Common/DeploymentUtility.cs b/test/E2ETests/Common/DeploymentUtility.cs index ac2766a9cd..431f93a9ca 100644 --- a/test/E2ETests/Common/DeploymentUtility.cs +++ b/test/E2ETests/Common/DeploymentUtility.cs @@ -63,7 +63,7 @@ namespace E2ETests private static string APP_RELATIVE_PATH = Path.Combine("..", "..", "src", "MusicStore"); - public static Process StartApplication(StartParameters startParameters, string identityDbName, ILogger logger) + public static Process StartApplication(StartParameters startParameters, ILogger logger) { startParameters.ApplicationPath = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, APP_RELATIVE_PATH)); @@ -158,7 +158,7 @@ namespace E2ETests } else { - hostProcess = StartSelfHost(startParameters, identityDbName, logger); + hostProcess = StartSelfHost(startParameters, logger); } } @@ -205,6 +205,13 @@ namespace E2ETests var hostProcess = Process.Start(startInfo); logger.WriteInformation("Started {0}. Process Id : {1}", hostProcess.MainModule.FileName, hostProcess.Id); + + if (hostProcess.HasExited) + { + logger.WriteError("Host process {processName} exited with code {exitCode} or failed to start.", startInfo.FileName, hostProcess.ExitCode); + throw new Exception("Failed to start host"); + } + return hostProcess; } @@ -256,7 +263,7 @@ namespace E2ETests return hostProcess; } - private static Process StartSelfHost(StartParameters startParameters, string identityDbName, ILogger logger) + private static Process StartSelfHost(StartParameters startParameters, ILogger logger) { var commandName = startParameters.ServerType == ServerType.WebListener ? "web" : "kestrel"; logger.WriteInformation("Executing klr.exe --appbase {appPath} \"Microsoft.Framework.ApplicationHost\" {command}", startParameters.ApplicationPath, commandName); @@ -273,6 +280,12 @@ namespace E2ETests //Sometimes reading MainModule returns null if called immediately after starting process. Thread.Sleep(1 * 1000); + if (hostProcess.HasExited) + { + logger.WriteError("Host process {processName} exited with code {exitCode} or failed to start.", startInfo.FileName, hostProcess.ExitCode); + throw new Exception("Failed to start host"); + } + try { logger.WriteInformation("Started {fileName}. Process Id : {processId}", hostProcess.MainModule.FileName, hostProcess.Id); @@ -282,8 +295,6 @@ namespace E2ETests logger.WriteWarning("Cannot access 64 bit modules from a 32 bit process. Failed with following message.", win32Exception); } - WaitTillDbCreated(identityDbName, logger); - return hostProcess; } @@ -342,50 +353,6 @@ namespace E2ETests logger.WriteInformation("kpm bundle finished with exit code : {exitCode}", hostProcess.ExitCode); } - //In case of self-host application activation happens immediately unlike iis where activation happens on first request. - //So in self-host case, we need a way to block the first request until the application is initialized. In MusicStore application's case, - //identity DB creation is pretty much the last step of application setup. So waiting on this event will help us wait efficiently. - private static void WaitTillDbCreated(string identityDbName, ILogger logger) - { - var identityDBFullPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), identityDbName + ".mdf"); - if (File.Exists(identityDBFullPath)) - { - logger.WriteWarning("Database file '{mdf}' exists. Proceeding with the tests.", identityDBFullPath); - return; - } - - logger.WriteInformation("Watching for the DB file '{mdf}'", identityDBFullPath); - var dbWatch = new FileSystemWatcher(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), identityDbName + ".mdf"); - dbWatch.EnableRaisingEvents = true; - - try - { - if (!File.Exists(identityDBFullPath)) - { - //Wait for a maximum of 1 minute assuming the slowest cold start. - var watchResult = dbWatch.WaitForChanged(WatcherChangeTypes.Created, 60 * 1000); - if (watchResult.ChangeType == WatcherChangeTypes.Created) - { - //This event is fired immediately after the localdb file is created. Give it a while to finish populating data and start the application. - Thread.Sleep(5 * 1000); - logger.WriteInformation("Database file created '{mdf}'. Proceeding with the tests.", identityDBFullPath); - } - else - { - logger.WriteWarning("Database file '{mdf}' not created", identityDBFullPath); - } - } - } - catch (Exception exception) - { - logger.WriteWarning("Received this exception while watching for Database file", exception); - } - finally - { - dbWatch.Dispose(); - } - } - public static void CleanUpApplication(StartParameters startParameters, Process hostProcess, string musicStoreDbName, ILogger logger) { if (startParameters.ServerType == ServerType.IISNativeModule || diff --git a/test/E2ETests/Common/Helpers.cs b/test/E2ETests/Common/Helpers.cs index 63a328826b..495fa090da 100644 --- a/test/E2ETests/Common/Helpers.cs +++ b/test/E2ETests/Common/Helpers.cs @@ -31,8 +31,7 @@ namespace E2ETests if (exception.InnerException is HttpRequestException || exception.InnerException is WebException) { logger.WriteWarning("Failed to complete the request.", exception); - var waitTimeInMilliSeconds = (RunningOnMono ? 7 : 1) * 1000; - Thread.Sleep(waitTimeInMilliSeconds); //Wait for a second before retry + Thread.Sleep(7 * 1000); //Wait for a while before retry. } } } diff --git a/test/E2ETests/Implementation/OpenIdConnectLoginScenarios.cs b/test/E2ETests/Implementation/OpenIdConnectLoginScenarios.cs index 4b3ca83d22..9221b6f5c9 100644 --- a/test/E2ETests/Implementation/OpenIdConnectLoginScenarios.cs +++ b/test/E2ETests/Implementation/OpenIdConnectLoginScenarios.cs @@ -82,6 +82,25 @@ namespace E2ETests //This action requires admin permissions. If notifications are fired this permission is granted Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); _logger.WriteInformation("Middleware notifications were fired successfully"); + + _logger.WriteInformation("Verifying the OpenIdConnect logout flow.."); + response = _httpClient.GetAsync(string.Empty).Result; + ThrowIfResponseStatusNotOk(response); + responseContent = response.Content.ReadAsStringAsync().Result; + ValidateLayoutPage(responseContent); + formParameters = new List> + { + new KeyValuePair("__RequestVerificationToken", HtmlDOMHelper.RetrieveAntiForgeryToken(responseContent, "/Account/LogOff")), + }; + + content = new FormUrlEncodedContent(formParameters.ToArray()); + response = _httpClient.PostAsync("Account/LogOff", content).Result; + Assert.Null(_httpClientHandler.CookieContainer.GetCookies(new Uri(_applicationBaseUrl)).GetCookieWithName(".AspNet.Microsoft.AspNet.Identity.Application")); + Assert.Equal( + "https://login.windows.net/4afbc689-805b-48cf-a24c-d4aa3248a248/oauth2/logout", + response.RequestMessage.RequestUri.AbsoluteUri.Replace(response.RequestMessage.RequestUri.Query, string.Empty)); + queryItems = new ReadableStringCollection(QueryHelpers.ParseQuery(response.RequestMessage.RequestUri.Query)); + Assert.Equal(_applicationBaseUrl + "Account/Login", queryItems["post_logout_redirect_uri"]); } } } \ No newline at end of file diff --git a/test/E2ETests/Implementation/Scenarios.cs b/test/E2ETests/Implementation/Scenarios.cs index aa933ab85e..ac1fa8abd3 100644 --- a/test/E2ETests/Implementation/Scenarios.cs +++ b/test/E2ETests/Implementation/Scenarios.cs @@ -56,7 +56,9 @@ namespace E2ETests _logger.WriteInformation("Application initialization successful."); _logger.WriteInformation("Application runtime information"); - var runtimeInfo = _httpClient.GetAsync("runtimeinfo").Result.Content.ReadAsStringAsync().Result; + var runtimeResponse = _httpClient.GetAsync("runtimeinfo").Result; + ThrowIfResponseStatusNotOk(runtimeResponse); + var runtimeInfo = runtimeResponse.Content.ReadAsStringAsync().Result; _logger.WriteInformation(runtimeInfo); } diff --git a/test/E2ETests/NtlmAuthentationTest.cs b/test/E2ETests/NtlmAuthentationTest.cs index 221f8bbd3b..896f1a9b43 100644 --- a/test/E2ETests/NtlmAuthentationTest.cs +++ b/test/E2ETests/NtlmAuthentationTest.cs @@ -46,7 +46,7 @@ namespace E2ETests try { - hostProcess = DeploymentUtility.StartApplication(_startParameters, musicStoreDbName, _logger); + hostProcess = DeploymentUtility.StartApplication(_startParameters, _logger); _httpClientHandler = new HttpClientHandler() { UseDefaultCredentials = true }; _httpClient = new HttpClient(_httpClientHandler) { BaseAddress = new Uri(applicationBaseUrl) }; diff --git a/test/E2ETests/OpenIdConnectTests.cs b/test/E2ETests/OpenIdConnectTests.cs index f0b9c61c2e..6f637aa11e 100644 --- a/test/E2ETests/OpenIdConnectTests.cs +++ b/test/E2ETests/OpenIdConnectTests.cs @@ -11,7 +11,7 @@ namespace E2ETests { [ConditionalTheory] [FrameworkSkipCondition(RuntimeFrameworks.Mono)] - [InlineData(ServerType.IISExpress, RuntimeFlavor.CoreClr, RuntimeArchitecture.x86, "http://localhost:5001/")] + [InlineData(ServerType.IISExpress, RuntimeFlavor.DesktopClr, RuntimeArchitecture.x86, "http://localhost:5001/")] public void OpenIdConnect_OnX86(ServerType serverType, RuntimeFlavor runtimeFlavor, RuntimeArchitecture architecture, string applicationBaseUrl) { OpenIdConnectTestSuite(serverType, runtimeFlavor, architecture, applicationBaseUrl); @@ -55,7 +55,7 @@ namespace E2ETests try { - hostProcess = DeploymentUtility.StartApplication(_startParameters, musicStoreDbName, _logger); + hostProcess = DeploymentUtility.StartApplication(_startParameters, _logger); if (serverType == ServerType.IISNativeModule || serverType == ServerType.IIS) { // Accomodate the vdir name. diff --git a/test/E2ETests/PublishAndRunTests.cs b/test/E2ETests/PublishAndRunTests.cs index fa4928209f..b0d6f1ea69 100644 --- a/test/E2ETests/PublishAndRunTests.cs +++ b/test/E2ETests/PublishAndRunTests.cs @@ -65,7 +65,7 @@ namespace E2ETests try { - hostProcess = DeploymentUtility.StartApplication(_startParameters, musicStoreDbName, _logger); + hostProcess = DeploymentUtility.StartApplication(_startParameters, _logger); _httpClientHandler = new HttpClientHandler() { UseDefaultCredentials = true }; _httpClient = new HttpClient(_httpClientHandler) { BaseAddress = new Uri(applicationBaseUrl) }; diff --git a/test/E2ETests/SmokeTests.cs b/test/E2ETests/SmokeTests.cs index ea6053bdd6..5fb3b1492d 100644 --- a/test/E2ETests/SmokeTests.cs +++ b/test/E2ETests/SmokeTests.cs @@ -116,7 +116,7 @@ namespace E2ETests try { - hostProcess = DeploymentUtility.StartApplication(_startParameters, musicStoreDbName, _logger); + hostProcess = DeploymentUtility.StartApplication(_startParameters, _logger); if (serverType == ServerType.IISNativeModule || serverType == ServerType.IIS) { // Accomodate the vdir name. diff --git a/test/E2ETests/compiler/shared/Mocks/OpenIdConnect/OpenIdConnectNotifications.cs b/test/E2ETests/compiler/shared/Mocks/OpenIdConnect/OpenIdConnectNotifications.cs index f9ab9d681e..f29c0db73e 100644 --- a/test/E2ETests/compiler/shared/Mocks/OpenIdConnect/OpenIdConnectNotifications.cs +++ b/test/E2ETests/compiler/shared/Mocks/OpenIdConnect/OpenIdConnectNotifications.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Security.Claims; using System.Threading.Tasks; +using Microsoft.AspNet.Http; using Microsoft.AspNet.Security.Notifications; using Microsoft.AspNet.Security.OpenIdConnect; using Microsoft.IdentityModel.Protocols; @@ -58,6 +59,13 @@ namespace MusicStore.Mocks.OpenIdConnect (RedirectToIdentityProviderNotification context) { notificationsFired.Add(nameof(RedirectToIdentityProvider)); + + if (context.ProtocolMessage.RequestType == OpenIdConnectRequestType.LogoutRequest) + { + context.ProtocolMessage.PostLogoutRedirectUri = + context.Request.Scheme + "://" + context.Request.Host + context.Request.PathBase + new PathString("/Account/Login"); + } + await Task.FromResult(0); } }