diff --git a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs
index 58969aca1a..98d50d61b1 100644
--- a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs
+++ b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs
@@ -321,7 +321,6 @@ namespace Umbraco.Core.Migrations.Install
var database = scope.Database;
var dbSchema = new DatabaseSchemaCreator(database, _logger, _umbracoVersion, _globalSettings);
_databaseSchemaValidationResult = dbSchema.ValidateSchema();
- scope.Complete();
return _databaseSchemaValidationResult;
}
diff --git a/src/Umbraco.Infrastructure/Services/Implement/KeyValueService.cs b/src/Umbraco.Infrastructure/Services/Implement/KeyValueService.cs
index 12ca34b020..3030e9b818 100644
--- a/src/Umbraco.Infrastructure/Services/Implement/KeyValueService.cs
+++ b/src/Umbraco.Infrastructure/Services/Implement/KeyValueService.cs
@@ -19,7 +19,7 @@ namespace Umbraco.Core.Services.Implement
///
public string GetValue(string key)
{
- using (var scope = _scopeProvider.CreateScope())
+ using (var scope = _scopeProvider.CreateScope(autoComplete: true))
{
return _repository.Get(key)?.Value;
}
diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs
index ce192ec377..1970205ebc 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs
@@ -13,14 +13,17 @@ using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.Grid;
using Umbraco.Core.Hosting;
+using Umbraco.Core.Logging;
using Umbraco.Core.Services;
using Umbraco.Core.WebAssets;
using Umbraco.Extensions;
using Umbraco.Web.BackOffice.Filters;
using Umbraco.Web.Common.ActionResults;
using Umbraco.Web.Common.Attributes;
+using Umbraco.Web.Common.Filters;
using Umbraco.Web.Common.Security;
using Umbraco.Web.Models;
+using Umbraco.Web.Security;
using Umbraco.Web.WebAssets;
using Constants = Umbraco.Core.Constants;
@@ -40,6 +43,8 @@ namespace Umbraco.Web.BackOffice.Controllers
private readonly BackOfficeServerVariables _backOfficeServerVariables;
private readonly AppCaches _appCaches;
private readonly BackOfficeSignInManager _signInManager;
+ private readonly IWebSecurity _webSecurity;
+ private readonly ILogger _logger;
public BackOfficeController(
BackOfficeUserManager userManager,
@@ -51,7 +56,10 @@ namespace Umbraco.Web.BackOffice.Controllers
IGridConfig gridConfig,
BackOfficeServerVariables backOfficeServerVariables,
AppCaches appCaches,
- BackOfficeSignInManager signInManager)
+ BackOfficeSignInManager signInManager,
+ IWebSecurity webSecurity,
+ ILogger logger)
+
{
_userManager = userManager;
_runtimeMinifier = runtimeMinifier;
@@ -63,6 +71,8 @@ namespace Umbraco.Web.BackOffice.Controllers
_backOfficeServerVariables = backOfficeServerVariables;
_appCaches = appCaches;
_signInManager = signInManager;
+ _webSecurity = webSecurity;
+ _logger = logger;
}
[HttpGet]
@@ -76,6 +86,84 @@ namespace Umbraco.Web.BackOffice.Controllers
() => View(viewPath));
}
+ [HttpGet]
+ public async Task VerifyInvite(string invite)
+ {
+ //if you are hitting VerifyInvite, you're already signed in as a different user, and the token is invalid
+ //you'll exit on one of the return RedirectToAction(nameof(Default)) but you're still logged in so you just get
+ //dumped at the default admin view with no detail
+ if (_webSecurity.IsAuthenticated())
+ {
+ await _signInManager.SignOutAsync();
+ }
+
+ if (invite == null)
+ {
+ _logger.Warn("VerifyUser endpoint reached with invalid token: NULL");
+ return RedirectToAction(nameof(Default));
+ }
+
+ var parts = System.Net.WebUtility.UrlDecode(invite).Split('|');
+
+ if (parts.Length != 2)
+ {
+ _logger.Warn("VerifyUser endpoint reached with invalid token: {Invite}", invite);
+ return RedirectToAction(nameof(Default));
+ }
+
+ var token = parts[1];
+
+ var decoded = token.FromUrlBase64();
+ if (decoded.IsNullOrWhiteSpace())
+ {
+ _logger.Warn("VerifyUser endpoint reached with invalid token: {Invite}", invite);
+ return RedirectToAction(nameof(Default));
+ }
+
+ var id = parts[0];
+
+ var identityUser = await _userManager.FindByIdAsync(id);
+ if (identityUser == null)
+ {
+ _logger.Warn("VerifyUser endpoint reached with non existing user: {UserId}", id);
+ return RedirectToAction(nameof(Default));
+ }
+
+ var result = await _userManager.ConfirmEmailAsync(identityUser, decoded);
+
+ if (result.Succeeded == false)
+ {
+ _logger.Warn("Could not verify email, Error: {Errors}, Token: {Invite}", result.Errors.ToErrorMessage(), invite);
+ return new RedirectResult(Url.Action(nameof(Default)) + "#/login/false?invite=3");
+ }
+
+ //sign the user in
+ DateTime? previousLastLoginDate = identityUser.LastLoginDateUtc;
+ await _signInManager.SignInAsync(identityUser, false);
+ //reset the lastlogindate back to previous as the user hasn't actually logged in, to add a flag or similar to BackOfficeSignInManager would be a breaking change
+ identityUser.LastLoginDateUtc = previousLastLoginDate;
+ await _userManager.UpdateAsync(identityUser);
+
+ return new RedirectResult(Url.Action(nameof(Default)) + "#/login/false?invite=1");
+ }
+
+ ///
+ /// This Action is used by the installer when an upgrade is detected but the admin user is not logged in. We need to
+ /// ensure the user is authenticated before the install takes place so we redirect here to show the standard login screen.
+ ///
+ ///
+ [HttpGet]
+ [StatusCodeResult(System.Net.HttpStatusCode.ServiceUnavailable)]
+ public async Task AuthorizeUpgrade()
+ {
+ var viewPath = Path.Combine(_globalSettings.UmbracoPath, Umbraco.Core.Constants.Web.Mvc.BackOfficeArea, nameof(AuthorizeUpgrade) + ".cshtml");
+ return await RenderDefaultOrProcessExternalLoginAsync(
+ //The default view to render when there is no external login info or errors
+ () => View(viewPath),
+ //The IActionResult to perform if external login is successful
+ () => Redirect("/"));
+ }
+
///
/// Returns the JavaScript main file including all references found in manifests
///
@@ -97,8 +185,7 @@ namespace Umbraco.Web.BackOffice.Controllers
[HttpGet]
public Dictionary> LocalizedText(string culture = null)
{
- var securityHelper = _umbracoContextAccessor.GetRequiredUmbracoContext().Security;
- var isAuthenticated = securityHelper.IsAuthenticated();
+ var isAuthenticated = _webSecurity.IsAuthenticated();
var cultureInfo = string.IsNullOrWhiteSpace(culture)
//if the user is logged in, get their culture, otherwise default to 'en'
diff --git a/src/Umbraco.Web.UI.NetCore/Umbraco/UmbracoBackOffice/AuthorizeUpgrade.cshtml b/src/Umbraco.Web.UI.NetCore/Umbraco/UmbracoBackOffice/AuthorizeUpgrade.cshtml
new file mode 100644
index 0000000000..d9f39b544c
--- /dev/null
+++ b/src/Umbraco.Web.UI.NetCore/Umbraco/UmbracoBackOffice/AuthorizeUpgrade.cshtml
@@ -0,0 +1,72 @@
+@using Umbraco.Core
+@using Umbraco.Web.WebAssets
+@using Umbraco.Web.Common.Security
+@using Umbraco.Core.WebAssets
+@using Umbraco.Core.Configuration
+@using Umbraco.Core.Hosting
+@using Umbraco.Extensions
+@using Umbraco.Core.Logging
+@using Umbraco.Web.BackOffice.Controllers
+@inject BackOfficeSignInManager signInManager
+@inject BackOfficeServerVariables backOfficeServerVariables
+@inject IUmbracoVersion umbracoVersion
+@inject IHostingEnvironment hostingEnvironment
+@inject IGlobalSettings globalSettings
+@inject IRuntimeMinifier runtimeMinifier
+
+@{
+ var backOfficePath = globalSettings.GetBackOfficePath(hostingEnvironment);
+}
+
+
+
+
+
+
+
+
+
+ Umbraco
+
+ @Html.Raw(await runtimeMinifier.RenderCssHereAsync(BackOfficeWebAssets.UmbracoUpgradeCssBundleName))
+
+ @*Because we're lazy loading angular js, the embedded cloak style will not be loaded initially, but we need it*@
+
+
+
+
+
+
+
+
+
+ @{
+ var externalLoginUrl = Url.Action("ExternalLogin", "BackOffice", new
+ {
+ area = ViewData.GetUmbracoPath(),
+ //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")
+ });
+ }
+
+ @await Html.BareMinimumServerVariablesScriptAsync(backOfficeServerVariables)
+
+
+
+ @*And finally we can load in our angular app*@
+
+
+
+
+
diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs
index a93f8182cd..b963871a58 100644
--- a/src/Umbraco.Web/Editors/BackOfficeController.cs
+++ b/src/Umbraco.Web/Editors/BackOfficeController.cs
@@ -1,32 +1,21 @@
using System;
-using System.Collections.Generic;
-using System.Globalization;
using System.Linq;
-using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
-using System.Web.UI;
using Microsoft.AspNetCore.Identity;
using Microsoft.Owin.Security;
-using Newtonsoft.Json;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
-using Umbraco.Web.Models;
using Umbraco.Web.Mvc;
using Umbraco.Core.Services;
using Umbraco.Web.Features;
using Umbraco.Web.Security;
using Constants = Umbraco.Core.Constants;
-using Umbraco.Core.Configuration.Grid;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Hosting;
-using Umbraco.Core.WebAssets;
-using Umbraco.Extensions;
-using Umbraco.Web.Trees;
-using Umbraco.Web.WebAssets;
using BackOfficeIdentityUser = Umbraco.Core.BackOffice.BackOfficeIdentityUser;
namespace Umbraco.Web.Editors
@@ -77,85 +66,6 @@ namespace Umbraco.Web.Editors
protected IAuthenticationManager AuthenticationManager => OwinContext.Authentication;
- [HttpGet]
- public async Task VerifyInvite(string invite)
- {
- //if you are hitting VerifyInvite, you're already signed in as a different user, and the token is invalid
- //you'll exit on one of the return RedirectToAction("Default") but you're still logged in so you just get
- //dumped at the default admin view with no detail
- if(Security.IsAuthenticated())
- {
- AuthenticationManager.SignOut(
- Core.Constants.Security.BackOfficeAuthenticationType,
- Core.Constants.Security.BackOfficeExternalAuthenticationType);
- }
-
- if (invite == null)
- {
- Logger.Warn("VerifyUser endpoint reached with invalid token: NULL");
- return RedirectToAction("Default");
- }
-
- var parts = Server.UrlDecode(invite).Split('|');
-
- if (parts.Length != 2)
- {
- Logger.Warn("VerifyUser endpoint reached with invalid token: {Invite}", invite);
- return RedirectToAction("Default");
- }
-
- var token = parts[1];
-
- var decoded = token.FromUrlBase64();
- if (decoded.IsNullOrWhiteSpace())
- {
- Logger.Warn("VerifyUser endpoint reached with invalid token: {Invite}", invite);
- return RedirectToAction("Default");
- }
-
- var id = parts[0];
-
- var identityUser = await UserManager.FindByIdAsync(id);
- if (identityUser == null)
- {
- Logger.Warn("VerifyUser endpoint reached with non existing user: {UserId}", id);
- return RedirectToAction("Default");
- }
-
- var result = await UserManager.ConfirmEmailAsync(identityUser, decoded);
-
- if (result.Succeeded == false)
- {
- Logger.Warn("Could not verify email, Error: {Errors}, Token: {Invite}", result.Errors.ToErrorMessage(), invite);
- return new RedirectResult(Url.Action("Default") + "#/login/false?invite=3");
- }
-
- //sign the user in
- DateTime? previousLastLoginDate = identityUser.LastLoginDateUtc;
- await SignInManager.SignInAsync(identityUser, false, false);
- //reset the lastlogindate back to previous as the user hasn't actually logged in, to add a flag or similar to SignInManager would be a breaking change
- identityUser.LastLoginDateUtc = previousLastLoginDate;
- await UserManager.UpdateAsync(identityUser);
-
- return new RedirectResult(Url.Action("Default") + "#/login/false?invite=1");
- }
-
- ///
- /// This Action is used by the installer when an upgrade is detected but the admin user is not logged in. We need to
- /// ensure the user is authenticated before the install takes place so we redirect here to show the standard login screen.
- ///
- ///
- [HttpGet]
- [StatusCodeResult(System.Net.HttpStatusCode.ServiceUnavailable)]
- public async Task AuthorizeUpgrade()
- {
- return await RenderDefaultOrProcessExternalLoginAsync(
- //The default view to render when there is no external login info or errors
- () => View(GlobalSettings.GetBackOfficePath(_hostingEnvironment).EnsureEndsWith('/') + "Views/AuthorizeUpgrade.cshtml", new BackOfficeModel(_features, GlobalSettings, _umbracoVersion, _contentSettings, _hostingEnvironment, _runtimeSettings, _securitySettings)),
- //The ActionResult to perform if external login is successful
- () => Redirect("/"));
- }
-
// TODO: for converting to netcore, some examples:
// * https://github.com/dotnet/aspnetcore/blob/master/src/Identity/samples/IdentitySample.Mvc/Controllers/AccountController.cs