From a321d4d1b86233a7af9e97dc3a80411ec90519a6 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 2 Apr 2015 14:46:53 +1100 Subject: [PATCH] Allows the ability to use external logins to login to authorize upgrades, this means being able to add reserved paths at startup dynamically which is now built in as part of the AuthenticationOptionsExtensions for registering external logins for the back office. --- .../Umbraco/Views/AuthorizeUpgrade.cshtml | 10 ++- .../umbraco/Views/Default.cshtml | 2 +- .../Editors/BackOfficeController.cs | 83 ++++++++++++------- .../HtmlHelperBackOfficeExtensions.cs | 9 +- ...henticationDescriptionOptionsExtensions.cs | 31 ------- .../AuthenticationOptionsExtensions.cs | 67 +++++++++++++++ src/Umbraco.Web/Umbraco.Web.csproj | 2 +- src/Umbraco.Web/UmbracoModule.cs | 45 +++++++++- 8 files changed, 177 insertions(+), 72 deletions(-) delete mode 100644 src/Umbraco.Web/Security/Identity/AuthenticationDescriptionOptionsExtensions.cs create mode 100644 src/Umbraco.Web/Security/Identity/AuthenticationOptionsExtensions.cs diff --git a/src/Umbraco.Web.UI/Umbraco/Views/AuthorizeUpgrade.cshtml b/src/Umbraco.Web.UI/Umbraco/Views/AuthorizeUpgrade.cshtml index ac0b453bdb..dcad153fce 100644 --- a/src/Umbraco.Web.UI/Umbraco/Views/AuthorizeUpgrade.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/Views/AuthorizeUpgrade.cshtml @@ -51,7 +51,15 @@ - @Html.BareMinimumServerVariables(Url, (string)ViewBag.UmbracoPath) + @{ + var externalLoginUrl = Url.Action("ExternalLogin", "BackOffice", new + { + area = ViewBag.UmbracoPath, + //Custom redirect URL since we don't want to just redirect to the back office since this is for authing upgrades + redirectUrl = Url.Action("AuthorizeUpgrade", "BackOffice") + }); + } + @Html.BareMinimumServerVariables(Url, externalLoginUrl) @Html.AngularExternalLoginInfoValues((IEnumerable)ViewBag.ExternalSignInError) diff --git a/src/Umbraco.Web.UI/umbraco/Views/Default.cshtml b/src/Umbraco.Web.UI/umbraco/Views/Default.cshtml index a0c6bd225e..63a8688301 100644 --- a/src/Umbraco.Web.UI/umbraco/Views/Default.cshtml +++ b/src/Umbraco.Web.UI/umbraco/Views/Default.cshtml @@ -66,7 +66,7 @@ - @Html.BareMinimumServerVariables(Url, (string)ViewBag.UmbracoPath) + @Html.BareMinimumServerVariables(Url, Url.Action("ExternalLogin", "BackOffice", new { area = ViewBag.UmbracoPath })) @Html.AngularExternalLoginInfoValues((IEnumerable)ViewBag.ExternalSignInError) diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs index fe60eb647e..0313dd868f 100644 --- a/src/Umbraco.Web/Editors/BackOfficeController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeController.cs @@ -63,27 +63,9 @@ namespace Umbraco.Web.Editors /// public async Task Default() { - ViewBag.UmbracoPath = GlobalSettings.UmbracoMvcArea; - - //check if there's errors in the TempData, assign to view bag and render the view - if (TempData["ExternalSignInError"] != null) - { - ViewBag.ExternalSignInError = TempData["ExternalSignInError"]; - return View(GlobalSettings.Path.EnsureEndsWith('/') + "Views/Default.cshtml"); - } - - //First check if there's external login info, if there's not proceed as normal - var loginInfo = await OwinContext.Authentication.GetExternalLoginInfoAsync( - Core.Constants.Security.BackOfficeExternalAuthenticationType); - - if (loginInfo == null) - { - return View(GlobalSettings.Path.EnsureEndsWith('/') + "Views/Default.cshtml"); - } - - //we're just logging in with an external source, not linking accounts - return await ExternalSignInAsync(loginInfo); - + return await RenderDefaultOrProcessExternalLoginAsync( + () => View(GlobalSettings.Path.EnsureEndsWith('/') + "Views/Default.cshtml"), + () => View(GlobalSettings.Path.EnsureEndsWith('/') + "Views/Default.cshtml")); } /// @@ -92,11 +74,13 @@ namespace Umbraco.Web.Editors /// /// [HttpGet] - public ActionResult AuthorizeUpgrade() + public async Task AuthorizeUpgrade() { - ViewBag.UmbracoPath = GlobalSettings.UmbracoMvcArea; - - return View(GlobalSettings.Path.EnsureEndsWith('/') + "Views/AuthorizeUpgrade.cshtml"); + return await RenderDefaultOrProcessExternalLoginAsync( + //The default view to render when there is no external login info or errors + () => View(GlobalSettings.Path.EnsureEndsWith('/') + "Views/AuthorizeUpgrade.cshtml"), + //The ActionResult to perform if external login is successful + () => Redirect("/")); } /// @@ -422,11 +406,15 @@ namespace Umbraco.Web.Editors } [HttpPost] - public ActionResult ExternalLogin(string provider) + public ActionResult ExternalLogin(string provider, string redirectUrl = null) { + if (redirectUrl == null) + { + redirectUrl = Url.Action("Default", "BackOffice"); + } + // Request a redirect to the external login provider - return new ChallengeResult(provider, - Url.Action("Default", "BackOffice")); + return new ChallengeResult(provider, redirectUrl); } [UmbracoAuthorize] @@ -466,9 +454,42 @@ namespace Umbraco.Web.Editors return RedirectToLocal(Url.Action("Default", "BackOffice")); } - private async Task ExternalSignInAsync(ExternalLoginInfo loginInfo) + /// + /// Used by Default and AuthorizeUpgrade to render as per normal if there's no external login info, otherwise + /// process the external login info. + /// + /// + private async Task RenderDefaultOrProcessExternalLoginAsync(Func defaultResponse, Func externalSignInResponse) + { + if (defaultResponse == null) throw new ArgumentNullException("defaultResponse"); + if (externalSignInResponse == null) throw new ArgumentNullException("externalSignInResponse"); + + ViewBag.UmbracoPath = GlobalSettings.UmbracoMvcArea; + + //check if there's errors in the TempData, assign to view bag and render the view + if (TempData["ExternalSignInError"] != null) + { + ViewBag.ExternalSignInError = TempData["ExternalSignInError"]; + return defaultResponse(); + } + + //First check if there's external login info, if there's not proceed as normal + var loginInfo = await OwinContext.Authentication.GetExternalLoginInfoAsync( + Core.Constants.Security.BackOfficeExternalAuthenticationType); + + if (loginInfo == null) + { + return defaultResponse(); + } + + //we're just logging in with an external source, not linking accounts + return await ExternalSignInAsync(loginInfo, externalSignInResponse); + } + + private async Task ExternalSignInAsync(ExternalLoginInfo loginInfo, Func response) { if (loginInfo == null) throw new ArgumentNullException("loginInfo"); + if (response == null) throw new ArgumentNullException("response"); // Sign in the user with this external login provider if the user already has a login var user = await UserManager.FindAsync(loginInfo.Login); @@ -493,8 +514,8 @@ namespace Umbraco.Web.Editors Response.Cookies[Core.Constants.Security.BackOfficeExternalCookieName].Expires = DateTime.MinValue; } } - - return View(GlobalSettings.Path.EnsureEndsWith('/') + "Views/Default.cshtml"); + + return response(); } private async Task SignInAsync(BackOfficeIdentityUser user, bool isPersistent) diff --git a/src/Umbraco.Web/HtmlHelperBackOfficeExtensions.cs b/src/Umbraco.Web/HtmlHelperBackOfficeExtensions.cs index dbb79443d9..e43cd21a0e 100644 --- a/src/Umbraco.Web/HtmlHelperBackOfficeExtensions.cs +++ b/src/Umbraco.Web/HtmlHelperBackOfficeExtensions.cs @@ -19,13 +19,16 @@ namespace Umbraco.Web /// /// /// - /// + /// + /// The post url used to sign in with external logins - this can change depending on for what service the external login is service. + /// Example: normal back office login or authenticating upgrade login + /// /// /// /// These are the bare minimal server variables that are required for the application to start without being authenticated, /// we will load the rest of the server vars after the user is authenticated. /// - public static IHtmlString BareMinimumServerVariables(this HtmlHelper html, UrlHelper uri, string umbracoPath) + public static IHtmlString BareMinimumServerVariables(this HtmlHelper html, UrlHelper uri, string externalLoginsUrl) { var str = @"