Fixes: U4-6080 Server side email validation doesn't occur on existing members when they are saved

This commit is contained in:
Shannon
2015-01-07 13:17:44 +11:00
parent 699713346c
commit 282550f402
7 changed files with 164 additions and 132 deletions

View File

@@ -7,7 +7,7 @@ using Umbraco.Core.ObjectResolution;
namespace Umbraco.Tests.CoreStrings
{
[TestFixture]
[TestFixture]
public class StringExtensionsTests
{
[SetUp]

View 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."));
}
}
}

View File

@@ -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" />

View File

@@ -230,7 +230,6 @@ namespace Umbraco.Web.Editors
/// </summary>
/// <returns></returns>
[FileUploadCleanupFilter]
[MembershipProviderValidationFilter]
public MemberDisplay PostSave(
[ModelBinder(typeof(MemberBinder))]
MemberSave contentItem)

View File

@@ -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;
}
}
}

View File

@@ -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" />

View File

@@ -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;
}
}
}
}