Merge remote-tracking branch 'origin/netcore/netcore' into netcore/task/6666-auth-policies

# Conflicts:
#	src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs
#	src/Umbraco.Web.Common/Filters/UmbracoMemberAuthorizeFilter.cs
This commit is contained in:
Shannon
2020-11-24 00:46:38 +11:00
50 changed files with 699 additions and 368 deletions

View File

@@ -21,7 +21,13 @@ namespace Umbraco.Web.Website.Controllers
// [MergeParentContextViewData]
public abstract class SurfaceController : PluginController
{
private readonly IPublishedUrlProvider _publishedUrlProvider;
protected SurfaceController(IUmbracoContextAccessor umbracoContextAccessor, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, AppCaches appCaches, IProfilingLogger profilingLogger, IPublishedUrlProvider publishedUrlProvider)
: base(umbracoContextAccessor, databaseFactory, services, appCaches, profilingLogger)
{
PublishedUrlProvider = publishedUrlProvider;
}
protected IPublishedUrlProvider PublishedUrlProvider { get; }
/// <summary>
/// Gets the current page.
@@ -39,12 +45,6 @@ namespace Umbraco.Web.Website.Controllers
}
}
protected SurfaceController(IUmbracoContextAccessor umbracoContextAccessor, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, AppCaches appCaches, IProfilingLogger profilingLogger, IPublishedUrlProvider publishedUrlProvider)
: base(umbracoContextAccessor, databaseFactory, services, appCaches, profilingLogger)
{
_publishedUrlProvider = publishedUrlProvider;
}
/// <summary>
/// Redirects to the Umbraco page with the given id
/// </summary>
@@ -52,7 +52,7 @@ namespace Umbraco.Web.Website.Controllers
/// <returns></returns>
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(Guid contentKey)
{
return new RedirectToUmbracoPageResult(contentKey, _publishedUrlProvider, UmbracoContextAccessor);
return new RedirectToUmbracoPageResult(contentKey, PublishedUrlProvider, UmbracoContextAccessor);
}
/// <summary>
@@ -63,7 +63,7 @@ namespace Umbraco.Web.Website.Controllers
/// <returns></returns>
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(Guid contentKey, QueryString queryString)
{
return new RedirectToUmbracoPageResult(contentKey, queryString, _publishedUrlProvider, UmbracoContextAccessor);
return new RedirectToUmbracoPageResult(contentKey, queryString, PublishedUrlProvider, UmbracoContextAccessor);
}
/// <summary>
@@ -73,7 +73,7 @@ namespace Umbraco.Web.Website.Controllers
/// <returns></returns>
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(IPublishedContent publishedContent)
{
return new RedirectToUmbracoPageResult(publishedContent, _publishedUrlProvider, UmbracoContextAccessor);
return new RedirectToUmbracoPageResult(publishedContent, PublishedUrlProvider, UmbracoContextAccessor);
}
/// <summary>
@@ -84,7 +84,7 @@ namespace Umbraco.Web.Website.Controllers
/// <returns></returns>
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(IPublishedContent publishedContent, QueryString queryString)
{
return new RedirectToUmbracoPageResult(publishedContent, queryString, _publishedUrlProvider, UmbracoContextAccessor);
return new RedirectToUmbracoPageResult(publishedContent, queryString, PublishedUrlProvider, UmbracoContextAccessor);
}
/// <summary>
@@ -93,7 +93,7 @@ namespace Umbraco.Web.Website.Controllers
/// <returns></returns>
protected RedirectToUmbracoPageResult RedirectToCurrentUmbracoPage()
{
return new RedirectToUmbracoPageResult(CurrentPage, _publishedUrlProvider, UmbracoContextAccessor);
return new RedirectToUmbracoPageResult(CurrentPage, PublishedUrlProvider, UmbracoContextAccessor);
}
/// <summary>
@@ -103,7 +103,7 @@ namespace Umbraco.Web.Website.Controllers
/// <returns></returns>
protected RedirectToUmbracoPageResult RedirectToCurrentUmbracoPage(QueryString queryString)
{
return new RedirectToUmbracoPageResult(CurrentPage, queryString, _publishedUrlProvider, UmbracoContextAccessor);
return new RedirectToUmbracoPageResult(CurrentPage, queryString, PublishedUrlProvider, UmbracoContextAccessor);
}
/// <summary>

View File

@@ -0,0 +1,60 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.Security;
using Umbraco.Core.Persistence;
using Umbraco.Core.Security;
using Umbraco.Core.Services;
using Umbraco.Web.Common.Filters;
using Umbraco.Web.Routing;
namespace Umbraco.Web.Website.Controllers
{
public class UmbLoginController : SurfaceController
{
private readonly IUmbracoWebsiteSecurityAccessor _websiteSecurityAccessor;
public UmbLoginController(IUmbracoContextAccessor umbracoContextAccessor, IUmbracoDatabaseFactory databaseFactory,
ServiceContext services, AppCaches appCaches, IProfilingLogger profilingLogger, IPublishedUrlProvider publishedUrlProvider,
IUmbracoWebsiteSecurityAccessor websiteSecurityAccessor)
: base(umbracoContextAccessor, databaseFactory, services, appCaches, profilingLogger, publishedUrlProvider)
{
_websiteSecurityAccessor = websiteSecurityAccessor;
}
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateUmbracoFormRouteString]
public async Task<IActionResult> HandleLogin([Bind(Prefix = "loginModel")]LoginModel model)
{
if (ModelState.IsValid == false)
{
return CurrentUmbracoPage();
}
if (await _websiteSecurityAccessor.WebsiteSecurity.LoginAsync(model.Username, model.Password) == false)
{
// Don't add a field level error, just model level.
ModelState.AddModelError("loginModel", "Invalid username or password");
return CurrentUmbracoPage();
}
TempData["LoginSuccess"] = true;
// If there is a specified path to redirect to then use it.
if (model.RedirectUrl.IsNullOrWhiteSpace() == false)
{
// Validate the redirect url.
// If it's not a local url we'll redirect to the root of the current site.
return Redirect(Url.IsLocalUrl(model.RedirectUrl)
? model.RedirectUrl
: CurrentPage.AncestorOrSelf(1).Url(PublishedUrlProvider));
}
// Redirect to current page by default.
return RedirectToCurrentUmbracoPage();
}
}
}

View File

@@ -0,0 +1,55 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.Security;
using Umbraco.Core.Persistence;
using Umbraco.Core.Security;
using Umbraco.Core.Services;
using Umbraco.Web.Common.Filters;
using Umbraco.Web.Routing;
namespace Umbraco.Web.Website.Controllers
{
[UmbracoMemberAuthorize]
public class UmbLoginStatusController : SurfaceController
{
private readonly IUmbracoWebsiteSecurityAccessor _websiteSecurityAccessor;
public UmbLoginStatusController(IUmbracoContextAccessor umbracoContextAccessor,
IUmbracoDatabaseFactory databaseFactory, ServiceContext services, AppCaches appCaches,
IProfilingLogger profilingLogger, IPublishedUrlProvider publishedUrlProvider, IUmbracoWebsiteSecurityAccessor websiteSecurityAccessor)
: base(umbracoContextAccessor, databaseFactory, services, appCaches, profilingLogger, publishedUrlProvider)
{
_websiteSecurityAccessor = websiteSecurityAccessor;
}
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateUmbracoFormRouteString]
public async Task<IActionResult> HandleLogout([Bind(Prefix = "logoutModel")]PostRedirectModel model)
{
if (ModelState.IsValid == false)
{
return CurrentUmbracoPage();
}
if (_websiteSecurityAccessor.WebsiteSecurity.IsLoggedIn())
{
await _websiteSecurityAccessor.WebsiteSecurity.LogOutAsync();
}
TempData["LogoutSuccess"] = true;
// If there is a specified path to redirect to then use it.
if (model.RedirectUrl.IsNullOrWhiteSpace() == false)
{
return Redirect(model.RedirectUrl);
}
// Redirect to current page by default.
return RedirectToCurrentUmbracoPage();
}
}
}

View File

@@ -0,0 +1,64 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.Security;
using Umbraco.Core.Persistence;
using Umbraco.Core.Security;
using Umbraco.Core.Services;
using Umbraco.Web.Common.Filters;
using Umbraco.Web.Routing;
namespace Umbraco.Web.Website.Controllers
{
[UmbracoMemberAuthorize]
public class UmbProfileController : SurfaceController
{
private readonly IUmbracoWebsiteSecurityAccessor _websiteSecurityAccessor;
public UmbProfileController(IUmbracoContextAccessor umbracoContextAccessor, IUmbracoDatabaseFactory databaseFactory,
ServiceContext services, AppCaches appCaches, IProfilingLogger profilingLogger,
IPublishedUrlProvider publishedUrlProvider, IUmbracoWebsiteSecurityAccessor websiteSecurityAccessor)
: base(umbracoContextAccessor, databaseFactory, services, appCaches, profilingLogger, publishedUrlProvider)
{
_websiteSecurityAccessor = websiteSecurityAccessor;
}
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateUmbracoFormRouteString]
public async Task<IActionResult> HandleUpdateProfile([Bind(Prefix = "profileModel")] ProfileModel model)
{
if (ModelState.IsValid == false)
{
return CurrentUmbracoPage();
}
var result = await _websiteSecurityAccessor.WebsiteSecurity.UpdateMemberProfileAsync(model);
switch (result.Status)
{
case UpdateMemberProfileStatus.Success:
break;
case UpdateMemberProfileStatus.Error:
// Don't add a field level error, just model level.
ModelState.AddModelError("profileModel", result.ErrorMessage);
return CurrentUmbracoPage();
default:
throw new ArgumentOutOfRangeException();
}
TempData["ProfileUpdateSuccess"] = true;
// If there is a specified path to redirect to then use it.
if (model.RedirectUrl.IsNullOrWhiteSpace() == false)
{
return Redirect(model.RedirectUrl);
}
// Redirect to current page by default.
return RedirectToCurrentUmbracoPage();
}
}
}

View File

@@ -0,0 +1,93 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.Security;
using Umbraco.Core.Persistence;
using Umbraco.Core.Security;
using Umbraco.Core.Services;
using Umbraco.Web.Common.Filters;
using Umbraco.Web.Routing;
namespace Umbraco.Web.Website.Controllers
{
public class UmbRegisterController : SurfaceController
{
private readonly IUmbracoWebsiteSecurityAccessor _websiteSecurityAccessor;
public UmbRegisterController(IUmbracoContextAccessor umbracoContextAccessor,
IUmbracoDatabaseFactory databaseFactory, ServiceContext services, AppCaches appCaches,
IProfilingLogger profilingLogger, IPublishedUrlProvider publishedUrlProvider, IUmbracoWebsiteSecurityAccessor websiteSecurityAccessor)
: base(umbracoContextAccessor, databaseFactory, services, appCaches, profilingLogger, publishedUrlProvider)
{
_websiteSecurityAccessor = websiteSecurityAccessor;
}
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateUmbracoFormRouteString]
public async Task<IActionResult> HandleRegisterMember([Bind(Prefix = "registerModel")]RegisterModel model)
{
if (ModelState.IsValid == false)
{
return CurrentUmbracoPage();
}
// U4-10762 Server error with "Register Member" snippet (Cannot save member with empty name)
// If name field is empty, add the email address instead.
if (string.IsNullOrEmpty(model.Name) && string.IsNullOrEmpty(model.Email) == false)
{
model.Name = model.Email;
}
var result = await _websiteSecurityAccessor.WebsiteSecurity.RegisterMemberAsync(model, model.LoginOnSuccess);
switch (result)
{
case RegisterMemberStatus.Success:
TempData["FormSuccess"] = true;
// If there is a specified path to redirect to then use it.
if (model.RedirectUrl.IsNullOrWhiteSpace() == false)
{
return Redirect(model.RedirectUrl);
}
// Redirect to current page by default.
return RedirectToCurrentUmbracoPage();
case RegisterMemberStatus.InvalidUserName:
ModelState.AddModelError((model.UsernameIsEmail || model.Username == null)
? "registerModel.Email"
: "registerModel.Username",
"Username is not valid");
break;
case RegisterMemberStatus.InvalidPassword:
ModelState.AddModelError("registerModel.Password", "The password is not strong enough");
break;
case RegisterMemberStatus.InvalidEmail:
ModelState.AddModelError("registerModel.Email", "Email is invalid");
break;
case RegisterMemberStatus.DuplicateUserName:
ModelState.AddModelError((model.UsernameIsEmail || model.Username == null)
? "registerModel.Email"
: "registerModel.Username",
"A member with this username already exists.");
break;
case RegisterMemberStatus.DuplicateEmail:
ModelState.AddModelError("registerModel.Email", "A member with this e-mail address already exists");
break;
case RegisterMemberStatus.Error:
// Don't add a field level error, just model level.
ModelState.AddModelError("registerModel", $"An error occurred creating the member: {result}");
break;
default:
throw new ArgumentOutOfRangeException();
}
return CurrentUmbracoPage();
}
}
}