mirror of
https://github.com/LBPUnion/ProjectLighthouse.git
synced 2025-07-28 07:58:40 +00:00
Add email blacklist and refactor email validity checks
This commit is contained in:
parent
8b8756e6de
commit
980a2af3c6
7 changed files with 71 additions and 23 deletions
|
@ -129,9 +129,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.";
|
|||
if (message.StartsWith("/setemail ") && ServerConfiguration.Instance.Mail.MailEnabled)
|
||||
{
|
||||
string email = message[(message.IndexOf(" ", StringComparison.Ordinal)+1)..];
|
||||
if (!SanitizationHelper.IsValidEmail(email)) return this.Ok();
|
||||
|
||||
if (await this.database.Users.AnyAsync(u => u.EmailAddress == email)) return this.Ok();
|
||||
// Return a bad request on invalid email address
|
||||
if (!SMTPHelper.IsValidEmail(this.database, email)) return this.BadRequest();
|
||||
|
||||
UserEntity? user = await this.database.UserFromGameToken(token);
|
||||
if (user == null || user.EmailAddressVerified) return this.Ok();
|
||||
|
|
|
@ -39,7 +39,7 @@ public class SetEmailForm : BaseLayout
|
|||
UserEntity? user = await this.Database.Users.FirstOrDefaultAsync(u => u.UserId == token.UserId);
|
||||
if (user == null) return this.Redirect("~/login");
|
||||
|
||||
if (!SanitizationHelper.IsValidEmail(emailAddress))
|
||||
if (!SMTPHelper.IsValidEmail(this.Database, emailAddress))
|
||||
{
|
||||
this.Error = this.Translate(ErrorStrings.EmailInvalid);
|
||||
return this.Page();
|
||||
|
|
|
@ -38,9 +38,9 @@ public class PasswordResetRequestForm : BaseLayout
|
|||
return this.Page();
|
||||
}
|
||||
|
||||
if (!SanitizationHelper.IsValidEmail(email))
|
||||
if (!SMTPHelper.IsValidEmail(this.Database, email))
|
||||
{
|
||||
this.Error = "This email is in an invalid format";
|
||||
this.Error = "This email is invalid";
|
||||
return this.Page();
|
||||
}
|
||||
|
||||
|
|
|
@ -65,17 +65,13 @@ public class UserSettingsPage : BaseLayout
|
|||
}
|
||||
|
||||
if (ServerConfiguration.Instance.Mail.MailEnabled &&
|
||||
SanitizationHelper.IsValidEmail(email) &&
|
||||
SMTPHelper.IsValidEmail(this.Database, email) &&
|
||||
(this.User == this.ProfileUser || this.User.IsAdmin))
|
||||
{
|
||||
// if email hasn't already been used
|
||||
if (!await this.Database.Users.AnyAsync(u => u.EmailAddress != null && u.EmailAddress.ToLower() == email!.ToLower()))
|
||||
if (this.ProfileUser.EmailAddress != email)
|
||||
{
|
||||
if (this.ProfileUser.EmailAddress != email)
|
||||
{
|
||||
this.ProfileUser.EmailAddress = email;
|
||||
this.ProfileUser.EmailAddressVerified = false;
|
||||
}
|
||||
this.ProfileUser.EmailAddress = email;
|
||||
this.ProfileUser.EmailAddressVerified = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
25
ProjectLighthouse/Configuration/EnforceEmailConfiguration.cs
Normal file
25
ProjectLighthouse/Configuration/EnforceEmailConfiguration.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using YamlDotNet.Serialization;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Configuration;
|
||||
|
||||
public class EnforceEmailConfiguration : ConfigurationBase<EnforceEmailConfiguration>
|
||||
{
|
||||
public override int ConfigVersion { get; set; } = 2;
|
||||
|
||||
public override string ConfigName { get; set; } = "enforce-email.yml";
|
||||
|
||||
public override bool NeedsConfiguration { get; set; } = false;
|
||||
|
||||
public bool EnableEmailEnforcement { get; set; } = false;
|
||||
public bool EnableEmailBlacklist { get; set; } = false;
|
||||
|
||||
// No blacklist by default, add path to blacklist
|
||||
public string BlacklistFilePath { get; set; } = "";
|
||||
|
||||
public override ConfigurationBase<EnforceEmailConfiguration> Deserialize
|
||||
(IDeserializer deserializer, string text) =>
|
||||
deserializer.Deserialize<EnforceEmailConfiguration>(text);
|
||||
}
|
|
@ -2,6 +2,8 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using LBPUnion.ProjectLighthouse.Configuration;
|
||||
|
@ -21,6 +23,13 @@ public static class SMTPHelper
|
|||
|
||||
private const long emailCooldown = 1000 * 30;
|
||||
|
||||
// To prevent ReadAllLines() exception when BlacklistFilePath is empty
|
||||
private static readonly string[] blacklistFile =
|
||||
!string.IsNullOrWhiteSpace(EnforceEmailConfiguration.Instance.BlacklistFilePath)
|
||||
? File.ReadAllLines(EnforceEmailConfiguration.Instance.BlacklistFilePath) : [];
|
||||
|
||||
private static readonly HashSet<string> blacklistedDomains = new(blacklistFile);
|
||||
|
||||
private static bool CanSendMail(UserEntity user)
|
||||
{
|
||||
// Remove expired entries
|
||||
|
@ -69,6 +78,33 @@ public static class SMTPHelper
|
|||
recentlySentMail.TryAdd(user.UserId, TimeHelper.TimestampMillis + emailCooldown);
|
||||
}
|
||||
|
||||
// Accumulate checks to determine email validity
|
||||
public static bool IsValidEmail(DatabaseContext database, string email)
|
||||
{
|
||||
// Email should not be empty, should be an actual email, and shouldn't already be used by an account
|
||||
if (!string.IsNullOrWhiteSpace(email) && new EmailAddressAttribute().IsValid(email) && !EmailIsUsed(database, email).Result)
|
||||
{
|
||||
// Get domain after '@' character
|
||||
string domain = email.Split('@')[1];
|
||||
|
||||
// Don't even bother if there are no domains in blacklist (AKA file path is empty/invalid, or file itself is empty)
|
||||
if (EnforceEmailConfiguration.Instance.EnableEmailBlacklist && blacklistedDomains.Count > 0) return !DomainIsInBlacklist(domain);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if email is already in use by an account
|
||||
private static async Task<bool> EmailIsUsed(DatabaseContext database, string email)
|
||||
{
|
||||
return await database.Users.AnyAsync(u => u.EmailAddress != null && u.EmailAddress.ToLower() == email.ToLower());
|
||||
}
|
||||
|
||||
// Check if domain blacklist contains input domain
|
||||
private static bool DomainIsInBlacklist(string domain) => blacklistedDomains.Contains(domain);
|
||||
|
||||
public static void SendRegistrationEmail(IMailService mail, UserEntity user)
|
||||
{
|
||||
// There is intentionally no cooldown here because this is only used for registration
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
#nullable enable
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Helpers;
|
||||
|
||||
public static class SanitizationHelper
|
||||
{
|
||||
public static bool IsValidEmail(string? email) => !string.IsNullOrWhiteSpace(email) && new EmailAddressAttribute().IsValid(email);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue