mirror of
https://github.com/BrenBroZAYT/gameyfin.git
synced 2026-06-13 16:40:01 +00:00
Implemented field-level encryption for the database
This commit is contained in:
@@ -2,6 +2,9 @@
|
||||
<configuration default="false" name="GameyfinApplication" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot" nameIsGenerated="true">
|
||||
<option name="ACTIVE_PROFILES" value="dev" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" value="BUNDLED" />
|
||||
<envs>
|
||||
<env name="APP_KEY" value="8ODYedBBEA6qTd2Z/dZiWA==" />
|
||||
</envs>
|
||||
<module name="gameyfin.main" />
|
||||
<option name="SHORTEN_COMMAND_LINE" value="ARGS_FILE" />
|
||||
<option name="SPRING_BOOT_MAIN_CLASS" value="de.grimsi.gameyfin.GameyfinApplication" />
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package de.grimsi.gameyfin.config.entities
|
||||
|
||||
import de.grimsi.gameyfin.core.security.EncryptionConverter
|
||||
import jakarta.persistence.*
|
||||
import jakarta.validation.constraints.NotNull
|
||||
|
||||
@@ -12,7 +13,7 @@ class ConfigEntry(
|
||||
val key: String,
|
||||
|
||||
@NotNull
|
||||
@Lob
|
||||
@Column(name = "`value`")
|
||||
@Convert(converter = EncryptionConverter::class)
|
||||
var value: String
|
||||
)
|
||||
@@ -0,0 +1,58 @@
|
||||
package de.grimsi.gameyfin.core.security
|
||||
|
||||
import jakarta.persistence.AttributeConverter
|
||||
import jakarta.persistence.Converter
|
||||
import java.util.*
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
@Converter
|
||||
class EncryptionConverter : AttributeConverter<String, String> {
|
||||
|
||||
companion object {
|
||||
private const val ALGORITHM = "AES"
|
||||
private val SECRET_KEY: SecretKeySpec
|
||||
|
||||
init {
|
||||
val base64Key = System.getenv("APP_KEY")
|
||||
?: throw IllegalStateException("APP_KEY environment variable is not set or empty")
|
||||
|
||||
val decodedKey = Base64.getDecoder().decode(base64Key)
|
||||
|
||||
// Ensure the key length is valid for AES (128, 192, or 256 bits)
|
||||
if (decodedKey.size !in listOf(16, 24, 32)) {
|
||||
throw IllegalArgumentException("Invalid AES key length. Key must be 128, 192, or 256 bits.")
|
||||
}
|
||||
|
||||
SECRET_KEY = SecretKeySpec(decodedKey, ALGORITHM)
|
||||
}
|
||||
}
|
||||
|
||||
override fun convertToDatabaseColumn(attribute: String?): String? {
|
||||
return attribute?.let {
|
||||
try {
|
||||
val cipher = Cipher.getInstance(ALGORITHM).apply {
|
||||
init(Cipher.ENCRYPT_MODE, SECRET_KEY)
|
||||
}
|
||||
val encryptedBytes = cipher.doFinal(it.toByteArray())
|
||||
Base64.getEncoder().encodeToString(encryptedBytes)
|
||||
} catch (e: Exception) {
|
||||
throw RuntimeException("Error during encryption", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun convertToEntityAttribute(dbData: String?): String? {
|
||||
return dbData?.let {
|
||||
try {
|
||||
val cipher = Cipher.getInstance(ALGORITHM).apply {
|
||||
init(Cipher.DECRYPT_MODE, SECRET_KEY)
|
||||
}
|
||||
val decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(it))
|
||||
String(decryptedBytes)
|
||||
} catch (e: Exception) {
|
||||
throw RuntimeException("Error during decryption", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,14 @@
|
||||
package de.grimsi.gameyfin.users.entities
|
||||
|
||||
import jakarta.persistence.Entity
|
||||
import jakarta.persistence.FetchType
|
||||
import jakarta.persistence.Id
|
||||
import jakarta.persistence.OneToOne
|
||||
import de.grimsi.gameyfin.core.security.EncryptionConverter
|
||||
import jakarta.persistence.*
|
||||
import org.hibernate.annotations.CreationTimestamp
|
||||
import java.time.Instant
|
||||
|
||||
@Entity
|
||||
class PasswordResetToken(
|
||||
@Id
|
||||
@Convert(converter = EncryptionConverter::class)
|
||||
val token: String,
|
||||
|
||||
@OneToOne(targetEntity = User::class, fetch = FetchType.EAGER)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package de.grimsi.gameyfin.users.entities
|
||||
|
||||
import de.grimsi.gameyfin.core.security.EncryptionConverter
|
||||
import jakarta.annotation.Nullable
|
||||
import jakarta.persistence.*
|
||||
import jakarta.validation.constraints.NotNull
|
||||
@@ -23,6 +24,7 @@ class User(
|
||||
|
||||
@Nullable
|
||||
@Column(unique = true)
|
||||
@Convert(converter = EncryptionConverter::class)
|
||||
var email: String,
|
||||
|
||||
var email_confirmed: Boolean = false,
|
||||
|
||||
Reference in New Issue
Block a user