diff --git a/src/Umbraco.Core/Extensions/StringExtensions.cs b/src/Umbraco.Core/Extensions/StringExtensions.cs index 7c144138b0..e5eb0819e9 100644 --- a/src/Umbraco.Core/Extensions/StringExtensions.cs +++ b/src/Umbraco.Core/Extensions/StringExtensions.cs @@ -2,6 +2,7 @@ // See LICENSE for more details. using System.ComponentModel; +using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Security.Cryptography; @@ -1558,6 +1559,14 @@ public static class StringExtensions yield return sb.ToString(); } + /// + /// Checks whether a string is a valid email address. + /// + /// The string check + /// Returns a bool indicating whether the string is an email address. + public static bool IsEmail(this string? email) => + string.IsNullOrWhiteSpace(email) is false && new EmailAddressAttribute().IsValid(email); + // having benchmarked various solutions (incl. for/foreach, split and LINQ based ones), // this is by far the fastest way to find string needles in a string haystack public static int CountOccurrences(this string haystack, string needle) diff --git a/src/Umbraco.Core/Services/UserService.cs b/src/Umbraco.Core/Services/UserService.cs index 9eb5a3e5bb..657507f17b 100644 --- a/src/Umbraco.Core/Services/UserService.cs +++ b/src/Umbraco.Core/Services/UserService.cs @@ -917,7 +917,7 @@ internal partial class UserService : RepositoryService, IUserService { return UserOperationStatus.UserNameIsNotEmail; } - if (!IsEmailValid(model.Email)) + if (model.Email.IsEmail() is false) { return UserOperationStatus.InvalidEmail; } @@ -1136,7 +1136,7 @@ internal partial class UserService : RepositoryService, IUserService return UserOperationStatus.UserNameIsNotEmail; } - if (IsEmailValid(model.Email) is false) + if (model.Email.IsEmail() is false) { return UserOperationStatus.InvalidEmail; } @@ -1164,8 +1164,6 @@ internal partial class UserService : RepositoryService, IUserService return UserOperationStatus.Success; } - private static bool IsEmailValid(string email) => new EmailAddressAttribute().IsValid(email); - private List? GetIdsFromKeys(IEnumerable? guids, UmbracoObjectTypes type) { var keys = guids? diff --git a/src/Umbraco.Infrastructure/Services/MemberEditingService.cs b/src/Umbraco.Infrastructure/Services/MemberEditingService.cs index f9f155523a..061f3ecf62 100644 --- a/src/Umbraco.Infrastructure/Services/MemberEditingService.cs +++ b/src/Umbraco.Infrastructure/Services/MemberEditingService.cs @@ -225,6 +225,11 @@ internal sealed class MemberEditingService : IMemberEditingService return MemberEditingOperationStatus.InvalidUsername; } + if (model.Email.IsEmail() is false) + { + return MemberEditingOperationStatus.InvalidEmail; + } + if (password is not null) { IdentityResult validatePassword = await _memberManager.ValidatePasswordAsync(password); diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/ShortStringHelper/StringExtensionsTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/ShortStringHelper/StringExtensionsTests.cs index 58e06ba17b..9043a1a854 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/ShortStringHelper/StringExtensionsTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/ShortStringHelper/StringExtensionsTests.cs @@ -364,6 +364,16 @@ public class StringExtensionsTests TryIsFullPath(" ", false, false); // technically, a valid filename on Linux } + [TestCase("test@test.com", true)] + [TestCase("test@test", true)] + [TestCase("testtest.com", false)] + [TestCase("test@test.dk", true)] + [TestCase("test@test.se", true)] + [TestCase(null, false)] + [TestCase("", false)] + [TestCase(" ", false)] + public void IsEmail(string? email, bool isEmail) => Assert.AreEqual(isEmail, email.IsEmail()); + private static void TryIsFullPath(string path, bool expectedIsFull, bool expectedIsValid = true) { Assert.AreEqual(expectedIsFull, path.IsFullPath(), "IsFullPath('" + path + "')");