diff --git a/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdRegister.java b/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdRegister.java index ff52412..7a5ffd0 100644 --- a/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdRegister.java +++ b/src/main/java/net/frozenorb/apiv3/route/users/POSTUsersIdRegister.java @@ -10,18 +10,15 @@ import net.frozenorb.apiv3.model.NotificationTemplate; import net.frozenorb.apiv3.model.User; import net.frozenorb.apiv3.unsorted.BlockingCallback; import net.frozenorb.apiv3.unsorted.Notification; +import net.frozenorb.apiv3.util.EmailUtils; import net.frozenorb.apiv3.util.ErrorUtils; import java.util.Map; import java.util.concurrent.TimeUnit; -import java.util.regex.Pattern; public final class POSTUsersIdRegister implements Handler { - private static final Pattern VALID_EMAIL_PATTERN = Pattern.compile( - "^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$", - Pattern.CASE_INSENSITIVE - ); + public void handle(RoutingContext ctx) { BlockingCallback userCallback = new BlockingCallback<>(); @@ -41,11 +38,16 @@ public final class POSTUsersIdRegister implements Handler { JsonObject requestBody = ctx.getBodyAsJson(); String email = requestBody.getString("email"); - if (!VALID_EMAIL_PATTERN.matcher(email).matches()) { + if (!EmailUtils.isValidEmail(email)) { ErrorUtils.respondInvalidInput(ctx, email + " is not a valid email."); return; } + if (EmailUtils.isBannedEmailDomain(email)) { + ErrorUtils.respondInvalidInput(ctx, email + " is from a blacklisted domain."); + return; + } + if (user.getPendingEmailToken() != null && (System.currentTimeMillis() - user.getPendingEmailTokenSetAt().toEpochMilli()) < TimeUnit.DAYS.toMillis(2)) { ErrorUtils.respondGeneric(ctx, 200, "We just recently sent you a confirmation email. Please wait before trying to register again."); return; diff --git a/src/main/java/net/frozenorb/apiv3/util/EmailUtils.java b/src/main/java/net/frozenorb/apiv3/util/EmailUtils.java new file mode 100644 index 0000000..0792aa7 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/util/EmailUtils.java @@ -0,0 +1,51 @@ +package net.frozenorb.apiv3.util; + +import com.google.common.collect.ImmutableSet; +import io.vertx.core.http.HttpClient; +import io.vertx.core.http.HttpClientOptions; +import lombok.experimental.UtilityClass; +import lombok.extern.slf4j.Slf4j; +import net.frozenorb.apiv3.APIv3; + +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; + +@Slf4j +@UtilityClass +public class EmailUtils { + + private static final Pattern VALID_EMAIL_PATTERN = Pattern.compile( + "^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$", + Pattern.CASE_INSENSITIVE + ); + private static final HttpClient httpsClient = APIv3.getVertxInstance().createHttpClient(new HttpClientOptions().setSsl(true).setTrustAll(true)); + private static Set bannedEmailDomains = ImmutableSet.of(); + + static { + updateBannedEmailDomains(); + APIv3.getVertxInstance().setPeriodic(TimeUnit.MINUTES.toMillis(10), (id) -> updateBannedEmailDomains()); + } + + private static void updateBannedEmailDomains() { + httpsClient.get(443, "raw.githubusercontent.com", "/martenson/disposable-email-domains/master/disposable_email_blacklist.conf", (response) -> { + response.bodyHandler((body) -> bannedEmailDomains = ImmutableSet.copyOf(body.toString().split("\n"))); + response.exceptionHandler(Throwable::printStackTrace); + }).end(); + } + + public static boolean isBannedEmailDomain(String email) { + if (email == null) { + return false; + } + + String[] split = email.split("@"); + String domain = split[1]; + return split.length == 2 && bannedEmailDomains.contains(domain.toLowerCase()); + } + + public static boolean isValidEmail(String email) { + return VALID_EMAIL_PATTERN.matcher(email).matches(); + } + +} \ No newline at end of file