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 + "')");