From 84d09f6dbb0700140390d50e02a1d4aed1e55f5b Mon Sep 17 00:00:00 2001 From: Swordsteel Date: Thu, 2 Jan 2025 05:55:35 +0100 Subject: [PATCH] replace local jwt with library version - update ConfigurationController to handle hlaeja jwt instead of jwtService - update MeasurementController to handle hlaeja jwt instead of jwtService - add dependency for hlaeja jwt - remove dependencies for jjwt - remove JwtService.kt - remove PublicKeyProvider.kt - remove jwt key property explanation from additional-spring-configuration-metadata.json --- .gitignore | 4 +- README.md | 1 + build.gradle.kts | 4 +- gradle.properties | 2 +- .../controller/ConfigurationController.kt | 13 ++++-- .../controller/MeasurementController.kt | 16 +++++-- .../kotlin/ltd/hlaeja/service/JwtService.kt | 42 ------------------- .../ltd/hlaeja/util/PublicKeyProvider.kt | 35 ---------------- ...itional-spring-configuration-metadata.json | 5 --- src/test/resources/cert/valid-public-key.pem | 9 ++++ 10 files changed, 36 insertions(+), 95 deletions(-) delete mode 100644 src/main/kotlin/ltd/hlaeja/service/JwtService.kt delete mode 100644 src/main/kotlin/ltd/hlaeja/util/PublicKeyProvider.kt create mode 100644 src/test/resources/cert/valid-public-key.pem diff --git a/.gitignore b/.gitignore index 5e9b219..004b1eb 100644 --- a/.gitignore +++ b/.gitignore @@ -39,5 +39,5 @@ out/ ### Kotlin ### .kotlin -### cert ### -cert/ +#### Hlæja ### +/cert/ diff --git a/README.md b/README.md index 6343a42..c3d2b10 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Classes and endpoints, to shape and to steer, Devices and sensors, their purpose | spring.data.redis.host | ✓ | Redis host | | spring.data.redis.port | | Redis port | | spring.data.redis.database | ✓ | Redis database | +| spring.data.redis.password | ✗ | Redis password | | cache.time-to-live | | Cache time to live (minutes) | | jwt.public-key | ✓ | JWT public key | | device-registry.url | ✓ | Device Register URL | diff --git a/build.gradle.kts b/build.gradle.kts index f80cd25..74b0dd8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -17,14 +17,12 @@ dependencies { implementation(hlaeja.kotlinx.coroutines) implementation(hlaeja.micrometer.registry.influx) implementation(hlaeja.library.hlaeja.common.messages) + implementation(hlaeja.library.hlaeja.jwt) implementation(hlaeja.springboot.starter.actuator) implementation(hlaeja.springboot.starter.cache) implementation(hlaeja.springboot.starter.redis) implementation(hlaeja.springboot.starter.webflux) - runtimeOnly(hlaeja.jjwt.impl) - runtimeOnly(hlaeja.jjwt.jackson) - testImplementation(hlaeja.kotlin.test.junit5) testImplementation(hlaeja.kotlinx.coroutines.test) testImplementation(hlaeja.mockk) diff --git a/gradle.properties b/gradle.properties index dee7f38..46eafea 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ kotlin.code.style=official org.gradle.jvmargs=-Xmx1g version=0.4.0-SNAPSHOT -catalog=0.7.0 +catalog=0.8.0-SNAPSHOT docker.port.expose=8443 container.port.expose=8443 container.port.host=9000 diff --git a/src/main/kotlin/ltd/hlaeja/controller/ConfigurationController.kt b/src/main/kotlin/ltd/hlaeja/controller/ConfigurationController.kt index 4f282d0..f36e166 100644 --- a/src/main/kotlin/ltd/hlaeja/controller/ConfigurationController.kt +++ b/src/main/kotlin/ltd/hlaeja/controller/ConfigurationController.kt @@ -1,7 +1,9 @@ package ltd.hlaeja.controller +import java.util.UUID +import ltd.hlaeja.jwt.service.PublicJwtService import ltd.hlaeja.service.DeviceConfigurationService -import ltd.hlaeja.service.JwtService +import ltd.hlaeja.service.DeviceRegistryService import ltd.hlaeja.util.toDeviceResponse import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestHeader @@ -12,12 +14,17 @@ import org.springframework.web.bind.annotation.RestController @RequestMapping("/configuration") class ConfigurationController( private val configurationService: DeviceConfigurationService, - private val jwtService: JwtService, + private val deviceRegistry: DeviceRegistryService, + private val publicJwtService: PublicJwtService, ) { @GetMapping suspend fun getNodeConfiguration( @RequestHeader("Identity") identityToken: String, - ): Map = jwtService.getIdentity(identityToken) + ): Map = readIdentityToken(identityToken) + .let { deviceRegistry.getIdentityFromDevice(it) } .let { configurationService.getConfiguration(it.node).toDeviceResponse() } + + private fun readIdentityToken(identityToken: String): UUID = publicJwtService + .verify(identityToken) { claims -> UUID.fromString(claims.payload["device"] as String) } } diff --git a/src/main/kotlin/ltd/hlaeja/controller/MeasurementController.kt b/src/main/kotlin/ltd/hlaeja/controller/MeasurementController.kt index d6e5737..7134687 100644 --- a/src/main/kotlin/ltd/hlaeja/controller/MeasurementController.kt +++ b/src/main/kotlin/ltd/hlaeja/controller/MeasurementController.kt @@ -1,8 +1,11 @@ package ltd.hlaeja.controller +import java.util.UUID +import ltd.hlaeja.jwt.service.PublicJwtService import ltd.hlaeja.library.deviceData.MeasurementData +import ltd.hlaeja.library.deviceRegistry.Identity import ltd.hlaeja.service.DeviceDataService -import ltd.hlaeja.service.JwtService +import ltd.hlaeja.service.DeviceRegistryService import org.springframework.http.HttpStatus.CREATED import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PostMapping @@ -16,13 +19,14 @@ import org.springframework.web.bind.annotation.RestController @RequestMapping("/measurement") class MeasurementController( private val dataService: DeviceDataService, - private val jwtService: JwtService, + private val deviceRegistry: DeviceRegistryService, + private val publicJwtService: PublicJwtService, ) { @GetMapping suspend fun getNodeMeasurement( @RequestHeader("Identity") identityToken: String, - ): Map = jwtService.getIdentity(identityToken) + ): Map = readIdentityToken(identityToken) .let { dataService.getMeasurement(it.client, it.node).fields } @PostMapping @@ -31,7 +35,7 @@ class MeasurementController( @RequestHeader("Identity") identityToken: String, @RequestBody measurement: Map, ) { - return jwtService.getIdentity(identityToken) + return readIdentityToken(identityToken) .let { dataService.addMeasurement( it.client, @@ -45,4 +49,8 @@ class MeasurementController( ) } } + + private suspend fun readIdentityToken(identityToken: String): Identity.Response = publicJwtService + .verify(identityToken) { claims -> UUID.fromString(claims.payload["device"] as String) } + .let { deviceRegistry.getIdentityFromDevice(it) } } diff --git a/src/main/kotlin/ltd/hlaeja/service/JwtService.kt b/src/main/kotlin/ltd/hlaeja/service/JwtService.kt deleted file mode 100644 index 5f575e9..0000000 --- a/src/main/kotlin/ltd/hlaeja/service/JwtService.kt +++ /dev/null @@ -1,42 +0,0 @@ -package ltd.hlaeja.service - -import io.github.oshai.kotlinlogging.KotlinLogging -import io.jsonwebtoken.JwtException -import io.jsonwebtoken.JwtParser -import io.jsonwebtoken.Jwts -import java.util.UUID -import ltd.hlaeja.library.deviceRegistry.Identity -import ltd.hlaeja.property.JwtProperty -import ltd.hlaeja.util.PublicKeyProvider -import org.springframework.http.HttpStatus -import org.springframework.stereotype.Service -import org.springframework.web.server.ResponseStatusException - -private val log = KotlinLogging.logger {} - -@Service -class JwtService( - jwtProperty: JwtProperty, - private val deviceRegistry: DeviceRegistryService, -) { - - private val parser: JwtParser = Jwts.parser() - .verifyWith(PublicKeyProvider.load(jwtProperty.publicKey)) - .build() - - suspend fun getIdentity( - identityToken: String, - ): Identity.Response = try { - readIdentity(identityToken) - .let { deviceRegistry.getIdentityFromDevice(it) } - } catch (e: JwtException) { - log.warn { e.localizedMessage } - throw ResponseStatusException(HttpStatus.UNAUTHORIZED) - } - - private suspend fun readIdentity( - identity: String, - ): UUID = parser.parseSignedClaims(identity) - .let { UUID.fromString(it.payload["device"] as String) } - .also { log.debug { "Identified client device: $it" } } -} diff --git a/src/main/kotlin/ltd/hlaeja/util/PublicKeyProvider.kt b/src/main/kotlin/ltd/hlaeja/util/PublicKeyProvider.kt deleted file mode 100644 index ae5ab38..0000000 --- a/src/main/kotlin/ltd/hlaeja/util/PublicKeyProvider.kt +++ /dev/null @@ -1,35 +0,0 @@ -package ltd.hlaeja.util - -import java.security.KeyFactory -import java.security.interfaces.RSAPublicKey -import java.security.spec.X509EncodedKeySpec -import java.util.Base64.getDecoder -import ltd.hlaeja.exception.KeyProviderException - -object PublicKeyProvider { - - fun load( - pemFile: String, - ): RSAPublicKey = readPublicPemFile(pemFile) - .let(::makePublicKey) - - private fun makePublicKey( - publicKeyBytes: ByteArray, - ): RSAPublicKey = KeyFactory.getInstance("RSA") - .generatePublic(X509EncodedKeySpec(publicKeyBytes)) as RSAPublicKey - - private fun readPublicPemFile( - publicKey: String, - ): ByteArray = javaClass.classLoader - .getResource(publicKey) - ?.readText() - ?.let(::getPublicKeyByteArray) - ?: throw KeyProviderException("Could not load public key") - - private fun getPublicKeyByteArray( - keyText: String, - ): ByteArray = keyText.replace(Regex("[\r\n]+"), "") - .removePrefix("-----BEGIN PUBLIC KEY-----") - .removeSuffix("-----END PUBLIC KEY-----") - .let { getDecoder().decode(it) } -} diff --git a/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 35b28c5..60b76cb 100644 --- a/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -20,11 +20,6 @@ "type": "java.lang.String", "description": "Application build os version." }, - { - "name": "jwt.public-key", - "type": "java.lang.String", - "description": "Jwt public key file." - }, { "name": "device-registry.url", "type": "java.lang.String", diff --git a/src/test/resources/cert/valid-public-key.pem b/src/test/resources/cert/valid-public-key.pem new file mode 100644 index 0000000..cdb4982 --- /dev/null +++ b/src/test/resources/cert/valid-public-key.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3ZdlbISX729m5Ur1pVhg +XIvazcgUt0T0G32ML0tfwQ4aWTfwPII0SQRThaN6eiiBMRa0V8JMih1LT8JmGgst +dEx2nhMbVs/Osu8MhmP86c+HB/jPa1+0IR1TZKXoZoF52D2ZtoVf+mOWggAcm1R+ +V0Fj2cR/pgLkVt3GKUE2OokFC1iFUQFjThd1EzKcOv53TUek8FY8t66npQ4t3unD +bXZKoGXMuXCqZVykMbGTUQFRuT3NAOXRrJP+UDeY2uM2Yk98J+8FtLDYD6jpmyi0 +ghv6k8pK1w1n5NI3atVv5ZMUeQZ36AXL8SZi1105mamhLVQ0e0JixoMOPh7ziFyv +uwIDAQAB +-----END PUBLIC KEY-----