diff --git a/src/main/java/icu/samnyan/aqua/net/UserRegistrar.kt b/src/main/java/icu/samnyan/aqua/net/UserRegistrar.kt index 91d5b83d..9bfaf5b4 100644 --- a/src/main/java/icu/samnyan/aqua/net/UserRegistrar.kt +++ b/src/main/java/icu/samnyan/aqua/net/UserRegistrar.kt @@ -3,6 +3,7 @@ package icu.samnyan.aqua.net import ext.* import icu.samnyan.aqua.net.db.AquaNetUser import icu.samnyan.aqua.net.db.AquaNetUserRepo +import icu.samnyan.aqua.net.utils.GeoIP import icu.samnyan.aqua.net.utils.TurnstileService import jakarta.servlet.http.HttpServletRequest import org.springframework.security.crypto.password.PasswordEncoder @@ -15,7 +16,8 @@ import org.springframework.web.bind.annotation.RestController class UserRegistrar( val userRepo: AquaNetUserRepo, val hasher: PasswordEncoder, - val turnstileService: TurnstileService + val turnstileService: TurnstileService, + val geoIP: GeoIP ) { /** * Register a new user @@ -23,8 +25,10 @@ class UserRegistrar( @PostMapping("/register") suspend fun register(@RP username: Str, @RP email: Str, @RP password: Str, @RP turnstile: Str?, request: HttpServletRequest) { + val ip = geoIP.getIP(request) + // Check captcha - if (!turnstileService.validate(turnstile, request)) 400 > "Invalid captcha" + if (!turnstileService.validate(turnstile, ip)) 400 > "Invalid captcha" // Check if email is valid if (!email.isValidEmail()) 400 > "Invalid email" @@ -47,12 +51,14 @@ class UserRegistrar( if (password.length < 8) 400 > "Password too short" // GeoIP check to infer country - + val country = geoIP.getCountry(ip) val u = AquaNetUser(username = username, email = email, pwHash = hasher.encode(password), - regTime = millis(), lastLogin = millis()) + regTime = millis(), lastLogin = millis(), country = country) async { userRepo.save(u) } + // TODO: Send confirmation email + 200 > "User created" } } \ No newline at end of file diff --git a/src/main/java/icu/samnyan/aqua/net/utils/GeoIP.kt b/src/main/java/icu/samnyan/aqua/net/utils/GeoIP.kt index f4bfb5d3..b1ef5cb7 100644 --- a/src/main/java/icu/samnyan/aqua/net/utils/GeoIP.kt +++ b/src/main/java/icu/samnyan/aqua/net/utils/GeoIP.kt @@ -2,7 +2,9 @@ package icu.samnyan.aqua.net.utils import com.maxmind.geoip2.DatabaseReader import ext.Bool +import ext.Str import jakarta.annotation.PostConstruct +import jakarta.servlet.http.HttpServletRequest import org.slf4j.LoggerFactory import org.springframework.boot.context.properties.ConfigurationProperties import org.springframework.context.annotation.Configuration @@ -16,9 +18,8 @@ import java.nio.file.Files @Configuration @ConfigurationProperties(prefix = "aqua-net.geoip") class GeoIPProperties { - var enable: Bool = false - - lateinit var geoLitePath: String + var geoLitePath: Str = "data/GeoLite2-Country.mmdb" + var ipHeader: Str = "" } @Service @@ -30,8 +31,6 @@ class GeoIP( @PostConstruct fun onLoad() { - if (!props.enable) return - // Check path exists if (!File(props.geoLitePath).exists()) { log.error("GeoIP Service is enabled but GeoLite2 database is not found, trying to download from GitHub.") @@ -47,25 +46,33 @@ class GeoIP( } geoLite = DatabaseReader.Builder(File(props.geoLitePath)).build() + selfTest() + log.info("GeoIP Service Enabled") + } - // Self test + /** + * Test the connection of the GeoIP service on startup + */ + fun selfTest() { try { getCountry("1.1.1.1") } catch (e: Exception) { log.error("GeoIP Service Self Test Failed", e) throw e } - - log.info("GeoIP Service Enabled") } + /** + * Get the IP address from a request + */ + fun getIP(request: HttpServletRequest): Str = + if (props.ipHeader.isEmpty()) request.remoteAddr else request.getHeader(props.ipHeader) ?: request.remoteAddr + /** * Get the country code from an IP address */ - fun getCountry(ip: String): String + fun getCountry(ip: Str): Str { - if (!props.enable) return "" - return try { geoLite.country(InetAddress.getByName(ip)).country.isoCode } catch (e: Exception) { diff --git a/src/main/java/icu/samnyan/aqua/net/utils/Turnstile.kt b/src/main/java/icu/samnyan/aqua/net/utils/Turnstile.kt index 4ca24e57..84013050 100644 --- a/src/main/java/icu/samnyan/aqua/net/utils/Turnstile.kt +++ b/src/main/java/icu/samnyan/aqua/net/utils/Turnstile.kt @@ -19,8 +19,6 @@ class TurnstileProperties { var enable: Bool = false lateinit var secret: Str - - lateinit var ipHeader: Str } @Service @@ -28,12 +26,10 @@ class TurnstileService(val props: TurnstileProperties) { @Serializable data class Outcome(val success: Boolean) - suspend fun validate(captcha: Str?, request: HttpServletRequest): Boolean { + suspend fun validate(captcha: Str?, ip: Str): Boolean { if (!props.enable) return true if (captcha == null) return false - val ip = request.getHeader(props.ipHeader) ?: request.remoteAddr - val outcome: Outcome = HTTP.post("https://challenges.cloudflare.com/turnstile/v0/siteverify") { setBody( FormDataContent(Parameters.build {