mirror of https://github.com/hykilpikonna/AquaDX
[+] Upload pfp endpoint
parent
c9ac38de01
commit
441d7376cb
|
@ -74,6 +74,9 @@ dependencies {
|
||||||
implementation("io.jsonwebtoken:jjwt-api:0.12.5")
|
implementation("io.jsonwebtoken:jjwt-api:0.12.5")
|
||||||
runtimeOnly("io.jsonwebtoken:jjwt-impl:0.12.5")
|
runtimeOnly("io.jsonwebtoken:jjwt-impl:0.12.5")
|
||||||
runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.12.5")
|
runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.12.5")
|
||||||
|
|
||||||
|
// Content validation
|
||||||
|
implementation("org.apache.tika:tika-core:2.9.1")
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "icu.samnya"
|
group = "icu.samnya"
|
||||||
|
|
|
@ -8,6 +8,8 @@ import io.ktor.serialization.kotlinx.json.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
import org.apache.tika.Tika
|
||||||
|
import org.apache.tika.mime.MimeTypes
|
||||||
import org.springframework.http.HttpStatus
|
import org.springframework.http.HttpStatus
|
||||||
import org.springframework.web.bind.annotation.RequestBody
|
import org.springframework.web.bind.annotation.RequestBody
|
||||||
import org.springframework.web.bind.annotation.RequestHeader
|
import org.springframework.web.bind.annotation.RequestHeader
|
||||||
|
@ -44,6 +46,7 @@ operator fun Int.minus(message: String): Nothing {
|
||||||
val emailRegex = "^(?=.{1,64}@)[\\p{L}0-9_-]+(\\.[\\p{L}0-9_-]+)*@[^-][\\p{L}0-9-]+(\\.[\\p{L}0-9-]+)*(\\.[\\p{L}]{2,})$".toRegex()
|
val emailRegex = "^(?=.{1,64}@)[\\p{L}0-9_-]+(\\.[\\p{L}0-9_-]+)*@[^-][\\p{L}0-9-]+(\\.[\\p{L}0-9-]+)*(\\.[\\p{L}]{2,})$".toRegex()
|
||||||
fun Str.isValidEmail(): Bool = emailRegex.matches(this)
|
fun Str.isValidEmail(): Bool = emailRegex.matches(this)
|
||||||
|
|
||||||
|
// Global tools
|
||||||
val HTTP = HttpClient(CIO) {
|
val HTTP = HttpClient(CIO) {
|
||||||
install(ContentNegotiation) {
|
install(ContentNegotiation) {
|
||||||
json(Json {
|
json(Json {
|
||||||
|
@ -52,6 +55,8 @@ val HTTP = HttpClient(CIO) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val TIKA = Tika()
|
||||||
|
val MIMES = MimeTypes.getDefaultMimeTypes()
|
||||||
|
|
||||||
// Date and time
|
// Date and time
|
||||||
fun millis() = System.currentTimeMillis()
|
fun millis() = System.currentTimeMillis()
|
||||||
|
|
|
@ -4,6 +4,7 @@ import ext.*
|
||||||
import icu.samnyan.aqua.net.components.*
|
import icu.samnyan.aqua.net.components.*
|
||||||
import icu.samnyan.aqua.net.db.*
|
import icu.samnyan.aqua.net.db.*
|
||||||
import icu.samnyan.aqua.net.db.AquaUserServices.Companion.SETTING_FIELDS
|
import icu.samnyan.aqua.net.db.AquaUserServices.Companion.SETTING_FIELDS
|
||||||
|
import icu.samnyan.aqua.net.utils.PathProps
|
||||||
import icu.samnyan.aqua.net.utils.SUCCESS
|
import icu.samnyan.aqua.net.utils.SUCCESS
|
||||||
import icu.samnyan.aqua.sega.general.dao.CardRepository
|
import icu.samnyan.aqua.sega.general.dao.CardRepository
|
||||||
import icu.samnyan.aqua.sega.general.model.Card
|
import icu.samnyan.aqua.sega.general.model.Card
|
||||||
|
@ -12,8 +13,10 @@ import jakarta.servlet.http.HttpServletRequest
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder
|
import org.springframework.security.crypto.password.PasswordEncoder
|
||||||
import org.springframework.web.bind.annotation.RestController
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
import org.springframework.web.multipart.MultipartFile
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
import kotlin.io.path.writeBytes
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@API("/api/v2/user")
|
@API("/api/v2/user")
|
||||||
|
@ -28,8 +31,11 @@ class UserRegistrar(
|
||||||
val cardRepo: CardRepository,
|
val cardRepo: CardRepository,
|
||||||
val cardService: CardService,
|
val cardService: CardService,
|
||||||
val validator: AquaUserServices,
|
val validator: AquaUserServices,
|
||||||
val emailProps: EmailProperties
|
val emailProps: EmailProperties,
|
||||||
|
paths: PathProps
|
||||||
) {
|
) {
|
||||||
|
val portraitPath = paths.aquaNetPortrait.path()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// Random long with length 9-10
|
// Random long with length 9-10
|
||||||
// We chose 1e9 as the start because normal cards took 0...1e9-1
|
// We chose 1e9 as the start because normal cards took 0...1e9-1
|
||||||
|
@ -188,4 +194,22 @@ class UserRegistrar(
|
||||||
|
|
||||||
mapOf("keychip" to new)
|
mapOf("keychip" to new)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@API("/upload-pfp")
|
||||||
|
@Doc("Upload a profile picture for the user.", "Success message")
|
||||||
|
suspend fun uploadPfp(@RP token: Str, @RP file: MultipartFile) = jwt.auth(token) { u ->
|
||||||
|
// Processing the image would lead to many open factors for attack
|
||||||
|
// (e.g. the JFIF Pixel Flood attack that ImageIO is vulnerable to)
|
||||||
|
// So we check file magic, then store the image without any processing
|
||||||
|
val bytes = file.bytes
|
||||||
|
val mime = TIKA.detect(bytes) ?: (400 - "Invalid file type")
|
||||||
|
|
||||||
|
// Check if the file is an image
|
||||||
|
if (!mime.startsWith("image/")) 400 - "Invalid file type"
|
||||||
|
|
||||||
|
// Save the image
|
||||||
|
(portraitPath / "${u.auId}.${MIMES.forName(mime)?.extension ?: "jpg"}").writeBytes(bytes)
|
||||||
|
|
||||||
|
SUCCESS
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue