Fixes: U4-6080 Server side email validation doesn't occur on existing members when they are saved
This commit is contained in:
@@ -7,7 +7,7 @@ using Umbraco.Core.ObjectResolution;
|
||||
|
||||
namespace Umbraco.Tests.CoreStrings
|
||||
{
|
||||
[TestFixture]
|
||||
[TestFixture]
|
||||
public class StringExtensionsTests
|
||||
{
|
||||
[SetUp]
|
||||
|
||||
28
src/Umbraco.Tests/CoreStrings/StringValidationTests.cs
Normal file
28
src/Umbraco.Tests/CoreStrings/StringValidationTests.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Umbraco.Tests.CoreStrings
|
||||
{
|
||||
[TestFixture]
|
||||
public class StringValidationTests
|
||||
{
|
||||
[Test]
|
||||
public void Validate_Email_Address()
|
||||
{
|
||||
var foo = new EmailAddressAttribute();
|
||||
|
||||
Assert.IsTrue(foo.IsValid("someone@somewhere.com"));
|
||||
Assert.IsTrue(foo.IsValid("someone@somewhere.co.uk"));
|
||||
Assert.IsTrue(foo.IsValid("someone+tag@somewhere.net"));
|
||||
Assert.IsTrue(foo.IsValid("futureTLD@somewhere.fooo"));
|
||||
|
||||
Assert.IsTrue(foo.IsValid("abc@xyz.financial"));
|
||||
|
||||
Assert.IsFalse(foo.IsValid("fdsa"));
|
||||
Assert.IsFalse(foo.IsValid("fdsa@"));
|
||||
Assert.IsFalse(foo.IsValid("fdsa@fdsa"));
|
||||
Assert.IsFalse(foo.IsValid("fdsa@fdsa."));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -168,6 +168,7 @@
|
||||
<Compile Include="AngularIntegration\ServerVariablesParserTests.cs" />
|
||||
<Compile Include="AttemptTests.cs" />
|
||||
<Compile Include="Cache\CacheRefresherTests.cs" />
|
||||
<Compile Include="CoreStrings\StringValidationTests.cs" />
|
||||
<Compile Include="FrontEnd\UmbracoHelperTests.cs" />
|
||||
<Compile Include="Membership\DynamicMemberContentTests.cs" />
|
||||
<Compile Include="Membership\MembershipProviderBaseTests.cs" />
|
||||
|
||||
@@ -230,7 +230,6 @@ namespace Umbraco.Web.Editors
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[FileUploadCleanupFilter]
|
||||
[MembershipProviderValidationFilter]
|
||||
public MemberDisplay PostSave(
|
||||
[ModelBinder(typeof(MemberBinder))]
|
||||
MemberSave contentItem)
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Web.Http;
|
||||
using System.Web.Http.Controllers;
|
||||
using System.Web.Http.Filters;
|
||||
using System.Web.Security;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// This validates the submitted data in regards to the current membership provider
|
||||
/// </summary>
|
||||
internal class MembershipProviderValidationFilterAttribute : ActionFilterAttribute
|
||||
{
|
||||
public override void OnActionExecuting(HttpActionContext actionContext)
|
||||
{
|
||||
base.OnActionExecuting(actionContext);
|
||||
|
||||
//default provider!
|
||||
var membershipProvider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
|
||||
|
||||
var contentItem = (MemberSave) actionContext.ActionArguments["contentItem"];
|
||||
|
||||
var validEmail = ValidateUniqueEmail(contentItem, membershipProvider, actionContext);
|
||||
if (validEmail == false)
|
||||
{
|
||||
actionContext.ModelState.AddPropertyError(
|
||||
new ValidationResult("Email address is already in use", new[] {"value"}),
|
||||
string.Format("{0}email", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
|
||||
}
|
||||
|
||||
var validLogin = ValidateUniqueLogin(contentItem, membershipProvider, actionContext);
|
||||
if (validLogin == false)
|
||||
{
|
||||
actionContext.ModelState.AddPropertyError(
|
||||
new ValidationResult("Username is already in use", new[] { "value" }),
|
||||
string.Format("{0}login", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
|
||||
}
|
||||
}
|
||||
|
||||
internal bool ValidateUniqueLogin(MemberSave contentItem, MembershipProvider membershipProvider, HttpActionContext actionContext)
|
||||
{
|
||||
if (contentItem == null) throw new ArgumentNullException("contentItem");
|
||||
if (membershipProvider == null) throw new ArgumentNullException("membershipProvider");
|
||||
|
||||
int totalRecs;
|
||||
var existingByName = membershipProvider.FindUsersByName(contentItem.Username.Trim(), 0, int.MaxValue, out totalRecs);
|
||||
switch (contentItem.Action)
|
||||
{
|
||||
case ContentSaveAction.Save:
|
||||
|
||||
//ok, we're updating the member, we need to check if they are changing their login and if so, does it exist already ?
|
||||
if (contentItem.PersistedContent.Username.InvariantEquals(contentItem.Username.Trim()) == false)
|
||||
{
|
||||
//they are changing their login name
|
||||
if (existingByName.Cast<MembershipUser>().Select(x => x.UserName)
|
||||
.Any(x => x == contentItem.Username.Trim()))
|
||||
{
|
||||
//the user cannot use this login
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ContentSaveAction.SaveNew:
|
||||
//check if the user's login already exists
|
||||
if (existingByName.Cast<MembershipUser>().Select(x => x.UserName)
|
||||
.Any(x => x == contentItem.Username.Trim()))
|
||||
{
|
||||
//the user cannot use this login
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//we don't support this for members
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool ValidateUniqueEmail(MemberSave contentItem, MembershipProvider membershipProvider, HttpActionContext actionContext)
|
||||
{
|
||||
if (contentItem == null) throw new ArgumentNullException("contentItem");
|
||||
if (membershipProvider == null) throw new ArgumentNullException("membershipProvider");
|
||||
|
||||
if (membershipProvider.RequiresUniqueEmail == false)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int totalRecs;
|
||||
var existingByEmail = membershipProvider.FindUsersByEmail(contentItem.Email.Trim(), 0, int.MaxValue, out totalRecs);
|
||||
switch (contentItem.Action)
|
||||
{
|
||||
case ContentSaveAction.Save:
|
||||
//ok, we're updating the member, we need to check if they are changing their email and if so, does it exist already ?
|
||||
if (contentItem.PersistedContent.Email.InvariantEquals(contentItem.Email.Trim()) == false)
|
||||
{
|
||||
//they are changing their email
|
||||
if (existingByEmail.Cast<MembershipUser>().Select(x => x.Email)
|
||||
.Any(x => x.InvariantEquals(contentItem.Email.Trim())))
|
||||
{
|
||||
//the user cannot use this email
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ContentSaveAction.SaveNew:
|
||||
//check if the user's email already exists
|
||||
if (existingByEmail.Cast<MembershipUser>().Select(x => x.Email)
|
||||
.Any(x => x.InvariantEquals(contentItem.Email.Trim())))
|
||||
{
|
||||
//the user cannot use this email
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//we don't support this for members
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -397,7 +397,6 @@
|
||||
<Compile Include="Mvc\UmbracoViewPage.cs" />
|
||||
<Compile Include="Editors\LogController.cs" />
|
||||
<Compile Include="Editors\MacroController.cs" />
|
||||
<Compile Include="Editors\MembershipProviderValidationFilterAttribute.cs" />
|
||||
<Compile Include="Editors\MemberTypeController.cs" />
|
||||
<Compile Include="Editors\UpdateCheckController.cs" />
|
||||
<Compile Include="MembershipProviderExtensions.cs" />
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Net;
|
||||
using System.Web.Http;
|
||||
using System.Web.Http.Controllers;
|
||||
using System.Web.Http.ModelBinding;
|
||||
using System.Web.Security;
|
||||
@@ -12,6 +15,7 @@ using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Web.WebApi.Filters;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Web;
|
||||
|
||||
namespace Umbraco.Web.WebApi.Binders
|
||||
{
|
||||
@@ -194,6 +198,52 @@ namespace Umbraco.Web.WebApi.Binders
|
||||
/// </summary>
|
||||
internal class MemberValidationHelper : ContentItemValidationHelper<IMember, MemberSave>
|
||||
{
|
||||
/// <summary>
|
||||
/// We need to manually validate a few things here like email and login to make sure they are valid and aren't duplicates
|
||||
/// </summary>
|
||||
/// <param name="postedItem"></param>
|
||||
/// <param name="actionContext"></param>
|
||||
/// <returns></returns>
|
||||
protected override bool ValidatePropertyData(ContentItemBasic<ContentPropertyBasic, IMember> postedItem, HttpActionContext actionContext)
|
||||
{
|
||||
var memberSave = (MemberSave)postedItem;
|
||||
|
||||
if (memberSave.Username.IsNullOrWhiteSpace())
|
||||
{
|
||||
actionContext.ModelState.AddPropertyError(
|
||||
new ValidationResult("Invalid user name", new[] { "value" }),
|
||||
string.Format("{0}login", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
|
||||
}
|
||||
|
||||
if (memberSave.Email.IsNullOrWhiteSpace() || new EmailAddressAttribute().IsValid(memberSave.Email) == false)
|
||||
{
|
||||
actionContext.ModelState.AddPropertyError(
|
||||
new ValidationResult("Invalid email", new[] { "value" }),
|
||||
string.Format("{0}email", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
|
||||
}
|
||||
|
||||
//default provider!
|
||||
var membershipProvider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
|
||||
|
||||
var validEmail = ValidateUniqueEmail(memberSave, membershipProvider, actionContext);
|
||||
if (validEmail == false)
|
||||
{
|
||||
actionContext.ModelState.AddPropertyError(
|
||||
new ValidationResult("Email address is already in use", new[] { "value" }),
|
||||
string.Format("{0}email", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
|
||||
}
|
||||
|
||||
var validLogin = ValidateUniqueLogin(memberSave, membershipProvider, actionContext);
|
||||
if (validLogin == false)
|
||||
{
|
||||
actionContext.ModelState.AddPropertyError(
|
||||
new ValidationResult("Username is already in use", new[] { "value" }),
|
||||
string.Format("{0}login", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
|
||||
}
|
||||
|
||||
return base.ValidatePropertyData(postedItem, actionContext);
|
||||
}
|
||||
|
||||
protected override bool ValidateProperties(ContentItemBasic<ContentPropertyBasic, IMember> postedItem, HttpActionContext actionContext)
|
||||
{
|
||||
var propertiesToValidate = postedItem.Properties.ToList();
|
||||
@@ -206,6 +256,90 @@ namespace Umbraco.Web.WebApi.Binders
|
||||
|
||||
return ValidateProperties(propertiesToValidate.ToArray(), postedItem.PersistedContent.Properties.ToArray(), actionContext);
|
||||
}
|
||||
|
||||
internal bool ValidateUniqueLogin(MemberSave contentItem, MembershipProvider membershipProvider, HttpActionContext actionContext)
|
||||
{
|
||||
if (contentItem == null) throw new ArgumentNullException("contentItem");
|
||||
if (membershipProvider == null) throw new ArgumentNullException("membershipProvider");
|
||||
|
||||
int totalRecs;
|
||||
var existingByName = membershipProvider.FindUsersByName(contentItem.Username.Trim(), 0, int.MaxValue, out totalRecs);
|
||||
switch (contentItem.Action)
|
||||
{
|
||||
case ContentSaveAction.Save:
|
||||
|
||||
//ok, we're updating the member, we need to check if they are changing their login and if so, does it exist already ?
|
||||
if (contentItem.PersistedContent.Username.InvariantEquals(contentItem.Username.Trim()) == false)
|
||||
{
|
||||
//they are changing their login name
|
||||
if (existingByName.Cast<MembershipUser>().Select(x => x.UserName)
|
||||
.Any(x => x == contentItem.Username.Trim()))
|
||||
{
|
||||
//the user cannot use this login
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ContentSaveAction.SaveNew:
|
||||
//check if the user's login already exists
|
||||
if (existingByName.Cast<MembershipUser>().Select(x => x.UserName)
|
||||
.Any(x => x == contentItem.Username.Trim()))
|
||||
{
|
||||
//the user cannot use this login
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//we don't support this for members
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool ValidateUniqueEmail(MemberSave contentItem, MembershipProvider membershipProvider, HttpActionContext actionContext)
|
||||
{
|
||||
if (contentItem == null) throw new ArgumentNullException("contentItem");
|
||||
if (membershipProvider == null) throw new ArgumentNullException("membershipProvider");
|
||||
|
||||
if (membershipProvider.RequiresUniqueEmail == false)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int totalRecs;
|
||||
var existingByEmail = membershipProvider.FindUsersByEmail(contentItem.Email.Trim(), 0, int.MaxValue, out totalRecs);
|
||||
switch (contentItem.Action)
|
||||
{
|
||||
case ContentSaveAction.Save:
|
||||
//ok, we're updating the member, we need to check if they are changing their email and if so, does it exist already ?
|
||||
if (contentItem.PersistedContent.Email.InvariantEquals(contentItem.Email.Trim()) == false)
|
||||
{
|
||||
//they are changing their email
|
||||
if (existingByEmail.Cast<MembershipUser>().Select(x => x.Email)
|
||||
.Any(x => x.InvariantEquals(contentItem.Email.Trim())))
|
||||
{
|
||||
//the user cannot use this email
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ContentSaveAction.SaveNew:
|
||||
//check if the user's email already exists
|
||||
if (existingByEmail.Cast<MembershipUser>().Select(x => x.Email)
|
||||
.Any(x => x.InvariantEquals(contentItem.Email.Trim())))
|
||||
{
|
||||
//the user cannot use this email
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//we don't support this for members
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user