From b331d683ecb8d665837d48bffd75e855ad52ce3e Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Tue, 24 Nov 2020 11:33:46 +0100 Subject: [PATCH] Migrated member related partial views along with necessary methods from MembershipHelper into IUmbracoWebsiteSecurity. --- .../Models/Security}/LoginStatusModel.cs | 3 +- .../Security/IUmbracoWebsiteSecurity.cs | 20 ++ .../Security/UmbracoWebsiteSecurityTests.cs | 99 +++++++++ .../Umbraco.Web.UI.NetCore.csproj | 4 + .../Templates/EditProfile.cshtml | 16 +- .../PartialViewMacros/Templates/Login.cshtml | 17 +- .../Templates/LoginStatus.cshtml | 14 +- .../Templates/RegisterMember.cshtml | 15 +- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 35 +--- .../Extensions/HtmlHelperRenderExtensions.cs | 80 ++++--- .../Extensions/TypeLoaderExtensions.cs | 1 - .../Security/UmbracoWebsiteSecurity.cs | 196 +++++++++++++++++- src/Umbraco.Web/Umbraco.Web.csproj | 1 - 13 files changed, 381 insertions(+), 120 deletions(-) rename src/{Umbraco.Web/Models => Umbraco.Core/Models/Security}/LoginStatusModel.cs (96%) create mode 100644 src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Security/UmbracoWebsiteSecurityTests.cs rename src/{Umbraco.Web.UI/Umbraco => Umbraco.Web.UI.NetCore/umbraco}/PartialViewMacros/Templates/EditProfile.cshtml (83%) rename src/{Umbraco.Web.UI/Umbraco => Umbraco.Web.UI.NetCore/umbraco}/PartialViewMacros/Templates/Login.cshtml (75%) rename src/{Umbraco.Web.UI/Umbraco => Umbraco.Web.UI.NetCore/umbraco}/PartialViewMacros/Templates/LoginStatus.cshtml (63%) rename src/{Umbraco.Web.UI/Umbraco => Umbraco.Web.UI.NetCore/umbraco}/PartialViewMacros/Templates/RegisterMember.cshtml (92%) diff --git a/src/Umbraco.Web/Models/LoginStatusModel.cs b/src/Umbraco.Core/Models/Security/LoginStatusModel.cs similarity index 96% rename from src/Umbraco.Web/Models/LoginStatusModel.cs rename to src/Umbraco.Core/Models/Security/LoginStatusModel.cs index 4b699bab38..3978a84334 100644 --- a/src/Umbraco.Web/Models/LoginStatusModel.cs +++ b/src/Umbraco.Core/Models/Security/LoginStatusModel.cs @@ -1,7 +1,6 @@ using System.ComponentModel.DataAnnotations; - -namespace Umbraco.Web.Models +namespace Umbraco.Core.Models.Security { /// /// The model representing the status of a logged in member. diff --git a/src/Umbraco.Core/Security/IUmbracoWebsiteSecurity.cs b/src/Umbraco.Core/Security/IUmbracoWebsiteSecurity.cs index c302d45354..00124c4dce 100644 --- a/src/Umbraco.Core/Security/IUmbracoWebsiteSecurity.cs +++ b/src/Umbraco.Core/Security/IUmbracoWebsiteSecurity.cs @@ -6,6 +6,13 @@ namespace Umbraco.Core.Security { public interface IUmbracoWebsiteSecurity { + /// + /// Creates a model to use for registering new members with custom member properties + /// + /// Alias of member type for created member (default used if not provided). + /// Instance of + RegisterModel CreateRegistrationModel(string memberTypeAlias = null); + /// /// Registers a new member. /// @@ -14,6 +21,13 @@ namespace Umbraco.Core.Security /// Result of registration operation. Task RegisterMemberAsync(RegisterModel model, bool logMemberIn = true); + /// + /// Creates a new profile model filled in with the current members details if they are logged in which allows for editing + /// profile properties. + /// + /// Instance of + Task GetCurrentMemberProfileModelAsync(); + /// /// Updates the currently logged in member's profile. /// @@ -35,6 +49,12 @@ namespace Umbraco.Core.Security /// True if logged in, false if not. bool IsLoggedIn(); + /// + /// Returns the login status model of the currently logged in member. + /// + /// Instance of + Task GetCurrentLoginStatusAsync(); + /// /// Logs out the current member. /// diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Security/UmbracoWebsiteSecurityTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Security/UmbracoWebsiteSecurityTests.cs new file mode 100644 index 0000000000..d021d38c15 --- /dev/null +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/Security/UmbracoWebsiteSecurityTests.cs @@ -0,0 +1,99 @@ +using System.Linq; +using System.Security.Claims; +using System.Security.Principal; +using Microsoft.AspNetCore.Http; +using Moq; +using NUnit.Framework; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Security; +using Umbraco.Core.Security; +using Umbraco.Core.Services; +using Umbraco.Core.Strings; +using Umbraco.Tests.Common.Builders; +using Umbraco.Web.Website.Security; +using CoreConstants = Umbraco.Core.Constants; + +namespace Umbraco.Tests.UnitTests.Umbraco.Web.Website.Security +{ + [TestFixture] + public class UmbracoWebsiteSecurityTests + { + [Test] + public void Can_Create_Registration_Model_With_Default_Member_Type() + { + var sut = CreateUmbracoWebsiteSecurity(); + + var result = sut.CreateRegistrationModel(); + AssertRegisterModel(result); + } + + [Test] + public void Can_Create_Registration_Model_With_Custom_Member_Type() + { + const string Alias = "testAlias"; + var sut = CreateUmbracoWebsiteSecurity(Alias); + + var result = sut.CreateRegistrationModel(Alias); + AssertRegisterModel(result, Alias); + } + + [Test] + public void Can_Detected_Logged_In_User() + { + var sut = CreateUmbracoWebsiteSecurity(); + + var result = sut.IsLoggedIn(); + Assert.IsTrue(result); + } + + [Test] + public void Can_Detected_Anonymous_User() + { + var sut = CreateUmbracoWebsiteSecurity(isUserAuthenticated: false); + + var result = sut.IsLoggedIn(); + Assert.IsFalse(result); + } + + private static void AssertRegisterModel(RegisterModel result, string memberTypeAlias = CoreConstants.Conventions.MemberTypes.DefaultAlias) + { + Assert.AreEqual(memberTypeAlias, result.MemberTypeAlias); + Assert.AreEqual(1, result.MemberProperties.Count); + + var firstProperty = result.MemberProperties.First(); + Assert.AreEqual("title", firstProperty.Alias); + Assert.AreEqual("Title", firstProperty.Name); + Assert.AreEqual(string.Empty, firstProperty.Value); + } + + private IUmbracoWebsiteSecurity CreateUmbracoWebsiteSecurity(string memberTypeAlias = CoreConstants.Conventions.MemberTypes.DefaultAlias, bool isUserAuthenticated = true) + { + var mockHttpContextAccessor = new Mock(); + var mockHttpContext = new Mock(); + var mockIdentity = new Mock(); + mockIdentity.SetupGet(x => x.IsAuthenticated).Returns(isUserAuthenticated); + var mockPrincipal = new Mock(); + mockPrincipal.Setup(x => x.Identity).Returns(mockIdentity.Object); + mockHttpContext.Setup(m => m.User).Returns(mockPrincipal.Object); + mockHttpContextAccessor.SetupGet(x => x.HttpContext).Returns(mockHttpContext.Object); + + var mockMemberService = new Mock(); + + var mockMemberTypeService = new Mock(); + mockMemberTypeService + .Setup(x => x.Get(It.Is(y => y == memberTypeAlias))) + .Returns(CreateSimpleMemberType(memberTypeAlias)); + + var mockShortStringHelper = new Mock(); + + return new UmbracoWebsiteSecurity(mockHttpContextAccessor.Object, mockMemberService.Object, mockMemberTypeService.Object, mockShortStringHelper.Object); + } + + private IMemberType CreateSimpleMemberType(string alias) + { + var memberType = MemberTypeBuilder.CreateSimpleMemberType(alias); + memberType.SetMemberCanEditProperty("title", true); + return memberType; + } + } +} diff --git a/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj index 6cc92f18da..1c199f1e0a 100644 --- a/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj +++ b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj @@ -68,6 +68,10 @@ + + + + diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EditProfile.cshtml b/src/Umbraco.Web.UI.NetCore/umbraco/PartialViewMacros/Templates/EditProfile.cshtml similarity index 83% rename from src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EditProfile.cshtml rename to src/Umbraco.Web.UI.NetCore/umbraco/PartialViewMacros/Templates/EditProfile.cshtml index 576541ea4a..d7639ff09f 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EditProfile.cshtml +++ b/src/Umbraco.Web.UI.NetCore/umbraco/PartialViewMacros/Templates/EditProfile.cshtml @@ -1,14 +1,12 @@ -@using System.Web.Mvc.Html -@using Umbraco.Web -@using Umbraco.Web.Composing -@using Umbraco.Web.Controllers -@inherits Umbraco.Web.Macros.PartialViewMacroPage +@using Umbraco.Core.Security +@using Umbraco.Extensions +@using Umbraco.Web.Website.Controllers +@inherits Umbraco.Web.Common.Macros.PartialViewMacroPage +@inject IUmbracoWebsiteSecurityAccessor UmbracoWebsiteSecurityAccessor @{ - var profileModel = Current.MembershipHelper.GetCurrentMemberProfileModel(); + var profileModel = await UmbracoWebsiteSecurityAccessor.WebsiteSecurity.GetCurrentMemberProfileModelAsync(); - Html.EnableClientValidation(); - Html.EnableUnobtrusiveJavaScript(); var success = TempData["ProfileUpdateSuccess"] != null; } @@ -17,7 +15,7 @@ -@if (Current.MembershipHelper.IsLoggedIn() && profileModel != null) +@if (UmbracoWebsiteSecurityAccessor.WebsiteSecurity.IsLoggedIn() && profileModel != null) { if (success) { diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Login.cshtml b/src/Umbraco.Web.UI.NetCore/umbraco/PartialViewMacros/Templates/Login.cshtml similarity index 75% rename from src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Login.cshtml rename to src/Umbraco.Web.UI.NetCore/umbraco/PartialViewMacros/Templates/Login.cshtml index b50d1ac806..bd4c9e1e59 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Login.cshtml +++ b/src/Umbraco.Web.UI.NetCore/umbraco/PartialViewMacros/Templates/Login.cshtml @@ -1,17 +1,12 @@ -@using System.Web.Mvc.Html -@using Umbraco.Web -@using Umbraco.Web.Composing -@using Umbraco.Web.Models -@using Umbraco.Web.Controllers -@inherits Umbraco.Web.Macros.PartialViewMacroPage +@using Microsoft.AspNetCore.Http.Extensions +@using Umbraco.Core.Models.Security +@using Umbraco.Extensions +@using Umbraco.Web.Website.Controllers +@inherits Umbraco.Web.Common.Macros.PartialViewMacroPage @{ var loginModel = new LoginModel(); - loginModel.RedirectUrl = HttpContext.Current.Request.Url.AbsolutePath; - - Html.EnableClientValidation(); - Html.EnableUnobtrusiveJavaScript(); - + loginModel.RedirectUrl = Context.Request.GetDisplayUrl(); } diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/LoginStatus.cshtml b/src/Umbraco.Web.UI.NetCore/umbraco/PartialViewMacros/Templates/LoginStatus.cshtml similarity index 63% rename from src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/LoginStatus.cshtml rename to src/Umbraco.Web.UI.NetCore/umbraco/PartialViewMacros/Templates/LoginStatus.cshtml index 78b06151af..350f573ade 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/LoginStatus.cshtml +++ b/src/Umbraco.Web.UI.NetCore/umbraco/PartialViewMacros/Templates/LoginStatus.cshtml @@ -1,12 +1,12 @@ -@using System.Web.Mvc.Html -@using Umbraco.Web -@using Umbraco.Web.Composing -@using Umbraco.Web.Models -@using Umbraco.Web.Controllers -@inherits Umbraco.Web.Macros.PartialViewMacroPage +@using Umbraco.Core.Security +@using Umbraco.Core.Models.Security +@using Umbraco.Extensions +@using Umbraco.Web.Website.Controllers +@inherits Umbraco.Web.Common.Macros.PartialViewMacroPage +@inject IUmbracoWebsiteSecurityAccessor UmbracoWebsiteSecurityAccessor @{ - var loginStatusModel = Current.MembershipHelper.GetCurrentLoginStatus(); + var loginStatusModel = await UmbracoWebsiteSecurityAccessor.WebsiteSecurity.GetCurrentLoginStatusAsync(); var logoutModel = new PostRedirectModel(); @* diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml b/src/Umbraco.Web.UI.NetCore/umbraco/PartialViewMacros/Templates/RegisterMember.cshtml similarity index 92% rename from src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml rename to src/Umbraco.Web.UI.NetCore/umbraco/PartialViewMacros/Templates/RegisterMember.cshtml index 5e6230a294..9bbf630a21 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/RegisterMember.cshtml +++ b/src/Umbraco.Web.UI.NetCore/umbraco/PartialViewMacros/Templates/RegisterMember.cshtml @@ -1,8 +1,8 @@ -@using System.Web.Mvc.Html -@using Umbraco.Web -@using Umbraco.Web.Composing -@using Umbraco.Web.Controllers -@inherits Umbraco.Web.Macros.PartialViewMacroPage +@using Umbraco.Core.Security +@using Umbraco.Extensions +@using Umbraco.Web.Website.Controllers +@inherits Umbraco.Web.Common.Macros.PartialViewMacroPage +@inject IUmbracoWebsiteSecurityAccessor UmbracoWebsiteSecurityAccessor @{ @* @@ -12,7 +12,7 @@ var registerModel = Members.CreateRegistrationModel("Custom Member"); *@ - var registerModel = Current.MembershipHelper.CreateRegistrationModel(); + var registerModel = UmbracoWebsiteSecurityAccessor.WebsiteSecurity.CreateRegistrationModel(); @* Configurable here: @@ -31,9 +31,6 @@ @Html.ValidationMessageFor(m => registerModel.Username) *@ - Html.EnableClientValidation(); - Html.EnableUnobtrusiveJavaScript(); - var success = TempData["FormSuccess"] != null; } diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 93f13e73e0..c413712b22 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -78,9 +78,7 @@ - - - + @@ -178,22 +176,6 @@ Designer - - true - PreserveNewest - - - true - PreserveNewest - - - true - PreserveNewest - - - true - PreserveNewest - Designer @@ -257,21 +239,8 @@ False True - 9000 - / - http://localhost:9000/ - http://localhost:8700 - 8610 - / - http://localhost:8610 - http://localhost:8700 - 8800 - 8900 8910 / - http://localhost:8800 - http://localhost:8700 - http://localhost:8900 http://localhost:8910 False False @@ -354,4 +323,4 @@ - + \ No newline at end of file diff --git a/src/Umbraco.Web.Website/Extensions/HtmlHelperRenderExtensions.cs b/src/Umbraco.Web.Website/Extensions/HtmlHelperRenderExtensions.cs index a48ed435bf..b58fbc7f53 100644 --- a/src/Umbraco.Web.Website/Extensions/HtmlHelperRenderExtensions.cs +++ b/src/Umbraco.Web.Website/Extensions/HtmlHelperRenderExtensions.cs @@ -11,7 +11,6 @@ using Microsoft.AspNetCore.Html; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Mvc.ViewFeatures; -using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; using Umbraco.Core; using Umbraco.Core.Cache; @@ -214,7 +213,7 @@ namespace Umbraco.Extensions // /// // /// // /// - // public static IHtmlContent Action(this HtmlHelper htmlHelper, string actionName) + // public static IHtmlContent Action(this IHtmlHelper htmlHelper, string actionName) // where T : SurfaceController // { // return htmlHelper.Action(actionName, typeof(T)); @@ -274,7 +273,6 @@ namespace Umbraco.Extensions /// /// /// - /// /// public UmbracoForm( ViewContext viewContext, @@ -282,19 +280,15 @@ namespace Umbraco.Extensions string controllerName, string controllerAction, string area, - FormMethod method, object additionalRouteVals = null) : base(viewContext, htmlEncoder) { _viewContext = viewContext; - _method = method; _controllerName = controllerName; _encryptedString = EncryptionHelper.CreateEncryptedRouteString(GetRequiredService(viewContext), controllerName, controllerAction, area, additionalRouteVals); } - private readonly ViewContext _viewContext; - private readonly FormMethod _method; private bool _disposed; private readonly string _encryptedString; private readonly string _controllerName; @@ -330,7 +324,7 @@ namespace Umbraco.Extensions /// Name of the controller. /// The method. /// - public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, string controllerName, FormMethod method) + public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action, string controllerName, FormMethod method) { return html.BeginUmbracoForm(action, controllerName, null, new Dictionary(), method); } @@ -342,7 +336,7 @@ namespace Umbraco.Extensions /// /// /// - public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, string controllerName) + public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action, string controllerName) { return html.BeginUmbracoForm(action, controllerName, null, new Dictionary()); } @@ -356,7 +350,7 @@ namespace Umbraco.Extensions /// /// /// - public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, string controllerName, object additionalRouteVals, FormMethod method) + public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action, string controllerName, object additionalRouteVals, FormMethod method) { return html.BeginUmbracoForm(action, controllerName, additionalRouteVals, new Dictionary(), method); } @@ -369,7 +363,7 @@ namespace Umbraco.Extensions /// /// /// - public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, string controllerName, object additionalRouteVals) + public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action, string controllerName, object additionalRouteVals) { return html.BeginUmbracoForm(action, controllerName, additionalRouteVals, new Dictionary()); } @@ -384,7 +378,7 @@ namespace Umbraco.Extensions /// /// /// - public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, string controllerName, + public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action, string controllerName, object additionalRouteVals, object htmlAttributes, FormMethod method) @@ -401,7 +395,7 @@ namespace Umbraco.Extensions /// /// /// - public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, string controllerName, + public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action, string controllerName, object additionalRouteVals, object htmlAttributes) { @@ -418,7 +412,7 @@ namespace Umbraco.Extensions /// /// /// - public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, string controllerName, + public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action, string controllerName, object additionalRouteVals, IDictionary htmlAttributes, FormMethod method) @@ -440,7 +434,7 @@ namespace Umbraco.Extensions /// /// /// - public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, string controllerName, + public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action, string controllerName, object additionalRouteVals, IDictionary htmlAttributes) { @@ -460,7 +454,7 @@ namespace Umbraco.Extensions /// The surface controller to route to /// /// - public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, Type surfaceType, FormMethod method) + public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action, Type surfaceType, FormMethod method) { return html.BeginUmbracoForm(action, surfaceType, null, new Dictionary(), method); } @@ -472,7 +466,7 @@ namespace Umbraco.Extensions /// /// The surface controller to route to /// - public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, Type surfaceType) + public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action, Type surfaceType) { return html.BeginUmbracoForm(action, surfaceType, null, new Dictionary()); } @@ -485,7 +479,7 @@ namespace Umbraco.Extensions /// /// /// - public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, FormMethod method) + public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action, FormMethod method) where T : SurfaceController { return html.BeginUmbracoForm(action, typeof(T), method); @@ -498,7 +492,7 @@ namespace Umbraco.Extensions /// /// /// - public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action) + public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action) where T : SurfaceController { return html.BeginUmbracoForm(action, typeof(T)); @@ -513,7 +507,7 @@ namespace Umbraco.Extensions /// /// /// - public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, Type surfaceType, + public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action, Type surfaceType, object additionalRouteVals, FormMethod method) { return html.BeginUmbracoForm(action, surfaceType, additionalRouteVals, new Dictionary(), method); @@ -527,7 +521,7 @@ namespace Umbraco.Extensions /// The surface controller to route to /// /// - public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, Type surfaceType, + public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action, Type surfaceType, object additionalRouteVals) { return html.BeginUmbracoForm(action, surfaceType, additionalRouteVals, new Dictionary()); @@ -542,7 +536,7 @@ namespace Umbraco.Extensions /// /// /// - public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, object additionalRouteVals, FormMethod method) + public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action, object additionalRouteVals, FormMethod method) where T : SurfaceController { return html.BeginUmbracoForm(action, typeof(T), additionalRouteVals, method); @@ -556,7 +550,7 @@ namespace Umbraco.Extensions /// /// /// - public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, object additionalRouteVals) + public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action, object additionalRouteVals) where T : SurfaceController { return html.BeginUmbracoForm(action, typeof(T), additionalRouteVals); @@ -572,7 +566,7 @@ namespace Umbraco.Extensions /// /// /// - public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, Type surfaceType, + public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action, Type surfaceType, object additionalRouteVals, object htmlAttributes, FormMethod method) @@ -589,7 +583,7 @@ namespace Umbraco.Extensions /// /// /// - public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, Type surfaceType, + public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action, Type surfaceType, object additionalRouteVals, object htmlAttributes) { @@ -606,7 +600,7 @@ namespace Umbraco.Extensions /// /// /// - public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, + public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action, object additionalRouteVals, object htmlAttributes, FormMethod method) @@ -624,7 +618,7 @@ namespace Umbraco.Extensions /// /// /// - public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, + public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action, object additionalRouteVals, object htmlAttributes) where T : SurfaceController @@ -642,7 +636,7 @@ namespace Umbraco.Extensions /// /// /// - public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, Type surfaceType, + public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action, Type surfaceType, object additionalRouteVals, IDictionary htmlAttributes, FormMethod method) @@ -652,18 +646,19 @@ namespace Umbraco.Extensions if (string.IsNullOrWhiteSpace(action)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(action)); if (surfaceType == null) throw new ArgumentNullException(nameof(surfaceType)); - var area = ""; - var surfaceControllerTypeCollection = GetRequiredService(html); var surfaceController = surfaceControllerTypeCollection.SingleOrDefault(x => x == surfaceType); if (surfaceController == null) throw new InvalidOperationException("Could not find the surface controller of type " + surfaceType.FullName); var metaData = PluginController.GetMetadata(surfaceController); + + var area = string.Empty; if (metaData.AreaName.IsNullOrWhiteSpace() == false) { - //set the area to the plugin area + // Set the area to the plugin area area = metaData.AreaName; } + return html.BeginUmbracoForm(action, metaData.ControllerName, area, additionalRouteVals, htmlAttributes, method); } @@ -676,7 +671,7 @@ namespace Umbraco.Extensions /// /// /// - public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, Type surfaceType, + public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action, Type surfaceType, object additionalRouteVals, IDictionary htmlAttributes) { @@ -693,7 +688,7 @@ namespace Umbraco.Extensions /// /// /// - public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, + public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action, object additionalRouteVals, IDictionary htmlAttributes, FormMethod method) @@ -711,7 +706,7 @@ namespace Umbraco.Extensions /// /// /// - public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, + public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action, object additionalRouteVals, IDictionary htmlAttributes) where T : SurfaceController @@ -728,7 +723,7 @@ namespace Umbraco.Extensions /// /// /// - public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, string controllerName, string area, FormMethod method) + public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action, string controllerName, string area, FormMethod method) { return html.BeginUmbracoForm(action, controllerName, area, null, new Dictionary(), method); } @@ -741,7 +736,7 @@ namespace Umbraco.Extensions /// /// /// - public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, string controllerName, string area) + public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action, string controllerName, string area) { return html.BeginUmbracoForm(action, controllerName, area, null, new Dictionary()); } @@ -757,7 +752,7 @@ namespace Umbraco.Extensions /// /// /// - public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, string controllerName, string area, + public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action, string controllerName, string area, object additionalRouteVals, IDictionary htmlAttributes, FormMethod method) @@ -782,7 +777,7 @@ namespace Umbraco.Extensions /// /// /// - public static MvcForm BeginUmbracoForm(this HtmlHelper html, string action, string controllerName, string area, + public static MvcForm BeginUmbracoForm(this IHtmlHelper html, string action, string controllerName, string area, object additionalRouteVals, IDictionary htmlAttributes) { @@ -804,7 +799,7 @@ namespace Umbraco.Extensions /// /// This code is pretty much the same as the underlying MVC code that writes out the form /// - private static MvcForm RenderForm(this HtmlHelper htmlHelper, + private static MvcForm RenderForm(this IHtmlHelper htmlHelper, string formAction, FormMethod method, IDictionary htmlAttributes, @@ -837,12 +832,13 @@ namespace Umbraco.Extensions var htmlEncoder = GetRequiredService(htmlHelper); //new UmbracoForm: - var theForm = new UmbracoForm(htmlHelper.ViewContext, htmlEncoder, surfaceController, surfaceAction, area, method, additionalRouteVals); + var theForm = new UmbracoForm(htmlHelper.ViewContext, htmlEncoder, surfaceController, surfaceAction, area, additionalRouteVals); if (traditionalJavascriptEnabled) { htmlHelper.ViewContext.FormContext.FormData["FormId"] = tagBuilder.Attributes["id"]; } + return theForm; } @@ -859,7 +855,7 @@ namespace Umbraco.Extensions /// /// The HTML encoded value. /// - public static IHtmlContent If(this HtmlHelper html, bool test, string valueIfTrue) + public static IHtmlContent If(this IHtmlHelper html, bool test, string valueIfTrue) { return If(html, test, valueIfTrue, string.Empty); } @@ -874,7 +870,7 @@ namespace Umbraco.Extensions /// /// The HTML encoded value. /// - public static IHtmlContent If(this HtmlHelper html, bool test, string valueIfTrue, string valueIfFalse) + public static IHtmlContent If(this IHtmlHelper html, bool test, string valueIfTrue, string valueIfFalse) { return new HtmlString(HttpUtility.HtmlEncode(test ? valueIfTrue : valueIfFalse)); } diff --git a/src/Umbraco.Web.Website/Extensions/TypeLoaderExtensions.cs b/src/Umbraco.Web.Website/Extensions/TypeLoaderExtensions.cs index c01bdf7804..cdaa40ef6a 100644 --- a/src/Umbraco.Web.Website/Extensions/TypeLoaderExtensions.cs +++ b/src/Umbraco.Web.Website/Extensions/TypeLoaderExtensions.cs @@ -4,7 +4,6 @@ using Umbraco.Core.Composing; using Umbraco.Web.Common.Controllers; using Umbraco.Web.Website.Controllers; - namespace Umbraco.Extensions { /// diff --git a/src/Umbraco.Web.Website/Security/UmbracoWebsiteSecurity.cs b/src/Umbraco.Web.Website/Security/UmbracoWebsiteSecurity.cs index 90e80537ec..d110cf9661 100644 --- a/src/Umbraco.Web.Website/Security/UmbracoWebsiteSecurity.cs +++ b/src/Umbraco.Web.Website/Security/UmbracoWebsiteSecurity.cs @@ -1,32 +1,173 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Http; +using Umbraco.Core; +using Umbraco.Core.Models; using Umbraco.Core.Models.Security; using Umbraco.Core.Security; +using Umbraco.Core.Services; +using Umbraco.Core.Strings; +using Umbraco.Web.Models; namespace Umbraco.Web.Website.Security { public class UmbracoWebsiteSecurity : IUmbracoWebsiteSecurity { private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IMemberService _memberService; + private readonly IMemberTypeService _memberTypeService; + private readonly IShortStringHelper _shortStringHelper; - public UmbracoWebsiteSecurity(IHttpContextAccessor httpContextAccessor) + public UmbracoWebsiteSecurity(IHttpContextAccessor httpContextAccessor, + IMemberService memberService, + IMemberTypeService memberTypeService, + IShortStringHelper shortStringHelper) { _httpContextAccessor = httpContextAccessor; + _memberService = memberService; + _memberTypeService = memberTypeService; + _shortStringHelper = shortStringHelper; } /// + public RegisterModel CreateRegistrationModel(string memberTypeAlias = null) + { + var providedOrDefaultMemberTypeAlias = memberTypeAlias ?? Constants.Conventions.MemberTypes.DefaultAlias; + var memberType = _memberTypeService.Get(providedOrDefaultMemberTypeAlias); + if (memberType == null) + { + throw new InvalidOperationException($"Could not find a member type with alias: {providedOrDefaultMemberTypeAlias}."); + } + + var model = RegisterModel.CreateModel(); + model.MemberTypeAlias = providedOrDefaultMemberTypeAlias; + model.MemberProperties = GetMemberPropertiesViewModel(memberType); + return model; + } + + private List GetMemberPropertiesViewModel(IMemberType memberType, IMember member = null) + { + var viewProperties = new List(); + + var builtIns = ConventionsHelper.GetStandardPropertyTypeStubs(_shortStringHelper).Select(x => x.Key).ToArray(); + + var propertyTypes = memberType.PropertyTypes + .Where(x => builtIns.Contains(x.Alias) == false && memberType.MemberCanEditProperty(x.Alias)) + .OrderBy(p => p.SortOrder); + + foreach (var prop in propertyTypes) + { + var value = string.Empty; + if (member != null) + { + var propValue = member.Properties[prop.Alias]; + if (propValue != null && propValue.GetValue() != null) + { + value = propValue.GetValue().ToString(); + } + } + + var viewProperty = new UmbracoProperty + { + Alias = prop.Alias, + Name = prop.Name, + Value = value + }; + + // TODO: Perhaps one day we'll ship with our own EditorTempates but for now developers + // can just render their own. + + ////This is a rudimentary check to see what data template we should render + //// if developers want to change the template they can do so dynamically in their views or controllers + //// for a given property. + ////These are the default built-in MVC template types: “Boolean”, “Decimal”, “EmailAddress”, “HiddenInput”, “HTML”, “Object”, “String”, “Text”, and “Url” + //// by default we'll render a text box since we've defined that metadata on the UmbracoProperty.Value property directly. + //if (prop.DataTypeId == new Guid(Constants.PropertyEditors.TrueFalse)) + //{ + // viewProperty.EditorTemplate = "UmbracoBoolean"; + //} + //else + //{ + // switch (prop.DataTypeDatabaseType) + // { + // case DataTypeDatabaseType.Integer: + // viewProperty.EditorTemplate = "Decimal"; + // break; + // case DataTypeDatabaseType.Ntext: + // viewProperty.EditorTemplate = "Text"; + // break; + // case DataTypeDatabaseType.Date: + // case DataTypeDatabaseType.Nvarchar: + // break; + // } + //} + + viewProperties.Add(viewProperty); + } + + return viewProperties; + } + public Task RegisterMemberAsync(RegisterModel model, bool logMemberIn = true) { throw new System.NotImplementedException(); } + /// + public async Task GetCurrentMemberProfileModelAsync() + { + if (IsLoggedIn() == false) + { + return null; + } + + var member = GetCurrentPersistedMember(); + + // This shouldn't happen but will if the member is deleted in the back office while the member is trying + // to use the front-end! + if (member == null) + { + // Log them out since they've been removed + await LogOutAsync(); + + return null; + } + + var model = new ProfileModel + { + Name = member.Name, + MemberTypeAlias = member.ContentTypeAlias, + + // TODO: get ASP.NET Core Identity equiavlant of MemberShipUser in order to get common membership properties such as Email + // and UserName (see MembershipProviderExtensions.GetCurrentUserName()for legacy membership provider implementation). + + //Email = membershipUser.Email, + //UserName = membershipUser.UserName, + //Comment = membershipUser.Comment, + //IsApproved = membershipUser.IsApproved, + //IsLockedOut = membershipUser.IsLockedOut, + //LastLockoutDate = membershipUser.LastLockoutDate, + //CreationDate = membershipUser.CreationDate, + //LastLoginDate = membershipUser.LastLoginDate, + //LastActivityDate = membershipUser.LastActivityDate, + //LastPasswordChangedDate = membershipUser.LastPasswordChangedDate + }; + + var memberType = _memberTypeService.Get(member.ContentTypeId); + + model.MemberProperties = GetMemberPropertiesViewModel(memberType, member); + + return model; + } + /// public Task UpdateMemberProfileAsync(ProfileModel model) { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } /// @@ -36,10 +177,55 @@ namespace Umbraco.Web.Website.Security return httpContext?.User != null && httpContext.User.Identity.IsAuthenticated; } + /// + public async Task GetCurrentLoginStatusAsync() + { + var model = LoginStatusModel.CreateModel(); + + if (IsLoggedIn() == false) + { + model.IsLoggedIn = false; + return model; + } + + var member = GetCurrentPersistedMember(); + + // This shouldn't happen but will if the member is deleted in the back office while the member is trying + // to use the front-end! + if (member == null) + { + // Log them out since they've been removed. + await LogOutAsync(); + model.IsLoggedIn = false; + return model; + } + + model.Name = member.Name; + model.Username = member.Username; + model.Email = member.Email; + model.IsLoggedIn = true; + + return model; + } + + /// + /// Returns the currently logged in IMember object - this should never be exposed to the front-end since it's returning a business logic entity! + /// + /// + private IMember GetCurrentPersistedMember() + { + // TODO: get user name from ASP.NET Core Identity (see MembershipProviderExtensions.GetCurrentUserName() + // for legacy membership provider implementation). + var username = ""; + + // The result of this is cached by the MemberRepository + return _memberService.GetByUsername(username); + } + /// public Task LoginAsync(string username, string password) { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } /// @@ -51,7 +237,7 @@ namespace Umbraco.Web.Website.Security /// public bool IsMemberAuthorized(IEnumerable allowTypes = null, IEnumerable allowGroups = null, IEnumerable allowMembers = null) { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } } } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 8157a90715..456088c8cd 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -235,7 +235,6 @@ -