11 Commits

Author SHA1 Message Date
f52f1237a2 [RELEASE] - release version: 0.4.0 2025-01-02 07:24:12 +01:00
4130ba681c update addDevice to handle violates of foreign key in DeviceService 2025-01-02 06:53:20 +01:00
df9d2c59a4 replace local jwt with library version
- update DeviceController to handle hlaeja jwt instead of jwtService
- update mapper sign with hlaeja jwt instead of jwtService
- add dependency for hlaeja jwt
- remove dependencies for jjwt
- remove JwtService.kt
- remove PrivateKeyProvider.kt
- remove jwt key property explanation from additional-spring-configuration-metadata.json
2025-01-02 06:53:20 +01:00
7d4ebab8f8 [RELEASE] - bump version 2024-12-28 07:43:32 +01:00
7184854ed4 [RELEASE] - release version: 0.3.0 2024-12-28 07:43:29 +01:00
df8e1a7b48 update for getDevice
- add get device in DeviceController
- add get device in DeviceService
- update logging
- update and cleanup in README.md
2024-12-27 22:56:27 +01:00
e7dbbc7a78 [RELEASE] - bump version 2024-12-10 23:25:29 +01:00
2a69d06d7f [RELEASE] - release version: 0.2.0 2024-12-10 23:25:26 +01:00
d5746aa22d update for common messages v0.2.0
- change DeviceController addDevice to use toDeviceResponse
- add DeviceEntity.toDeviceResponse to Mapping.kt
2024-12-10 21:28:58 +01:00
5d36099738 update for common plugin v0.2.0
- rename keys folder to cert
- update build.gradle.kts to use certificate plugin
- update catalog version
2024-12-10 19:20:14 +01:00
5c2e2e617e [RELEASE] - bump version 2024-11-26 11:42:12 +01:00
21 changed files with 181 additions and 277 deletions

2
.gitignore vendored
View File

@@ -40,4 +40,4 @@ out/
.kotlin .kotlin
### Cert ### ### Cert ###
/keys/ /cert/

View File

@@ -5,14 +5,14 @@ Classes crafted, identities bestowed, Each device recorded, their functions unfo
## Properties for deployment ## Properties for deployment
| name | required | info | | name | required | info |
|------------------------|----------|-------------------------| |------------------------|:--------:|-------------------------|
| spring.profiles.active | * | Spring Boot environment | | spring.profiles.active | ✓ | Spring Boot environment |
| spring.r2dbc.url | * | Postgres host url | | spring.r2dbc.url | ✓ | Postgres host url |
| spring.r2dbc.username | * | Postgres username | | spring.r2dbc.username | ✓ | Postgres username |
| spring.r2dbc.password | ** | Postgres password | | spring.r2dbc.password | ✗ | Postgres password |
| jwt.private-key | | JWT private cert | | jwt.private-key | ✓ | JWT private cert |
Required: * can be stored as text, and ** need to be stored as secret. *Required: ✓ can be stored as text, and ✗ need to be stored as secret.*
## Releasing Service ## Releasing Service
@@ -20,34 +20,28 @@ Run `release.sh` script from `master` branch.
## Development Information ## Development Information
### Generate Private and Public RSA Key ### Private RSA Key
OpenSSL Project is dedicated to providing a simple installation of OpenSSL for Microsoft Windows. [Download](https://slproweb.com/products/Win32OpenSSL.html) This service uses RAS keys to create identities for devices. The private key is used here to generate identities, while the public key is used by **[Hlæja Device API](https://github.com/swordsteel/hlaeja-device-api)** to identify a device and accept data.
Generate an RSA private key, of size 2048, and output it to a file named `private_key.pem` in to `./keys` *For instructions on how to set these up, please refer to our [generate RSA key](https://github.com/swordsteel/hlaeja-development/blob/master/doc/rsa_key.md) documentation.*
```shell ### Global Setting
openssl genrsa -out private_key.pem 2048
The following global settings are used in Hlaeja Device Registry. You can configure these settings using either Gradle properties or alternatively environment variables.
*For instructions on how to set these up, please refer to our [set global settings](https://github.com/swordsteel/hlaeja-development/blob/master/doc/global_settings.md) documentation.*
#### Gradle Properties
```properties
repository.user=your_user
repository.token=your_token_value
``` ```
Extract the public key from `private_key.pem` from `./keys`, and output it to a file named `public_key.pem` in to `./keys` #### Environment Variables
```shell ```properties
openssl rsa -in private_key.pem -pubout -out public_key.pem REPOSITORY_USER=your_user
REPOSITORY_TOKEN=your_token_value
``` ```
### Global gradle properties
To authenticate with Gradle to access repositories that require authentication, you can set your user and token in the `gradle.properties` file.
Here's how you can do it:
1. Open or create the `gradle.properties` file in your Gradle user home directory:
- On Unix-like systems (Linux, macOS), this directory is typically `~/.gradle/`.
- On Windows, this directory is typically `C:\Users\<YourUsername>\.gradle\`.
2. Add the following lines to the `gradle.properties` file:
```properties
repository.user=your_user
repository.token=your_token_value
```
or use environment variables `REPOSITORY_USER` and `REPOSITORY_TOKEN`

View File

@@ -1,47 +1,38 @@
plugins { plugins {
alias(hlaeja.plugins.kotlin.jvm) alias(hlaeja.plugins.kotlin.jvm)
alias(hlaeja.plugins.kotlin.spring) alias(hlaeja.plugins.kotlin.spring)
alias(hlaeja.plugins.ltd.hlaeja.plugin.certificate)
alias(hlaeja.plugins.ltd.hlaeja.plugin.service) alias(hlaeja.plugins.ltd.hlaeja.plugin.service)
alias(hlaeja.plugins.spring.dependency.management) alias(hlaeja.plugins.spring.dependency.management)
alias(hlaeja.plugins.springframework.boot) alias(hlaeja.plugins.springframework.boot)
} }
dependencies { dependencies {
implementation(hlaeja.com.fasterxml.jackson.module.kotlin) implementation(hlaeja.fasterxml.jackson.module.kotlin)
implementation(hlaeja.jjwt.api)
implementation(hlaeja.kotlin.logging) implementation(hlaeja.kotlin.logging)
implementation(hlaeja.kotlin.reflect) implementation(hlaeja.kotlin.reflect)
implementation(hlaeja.kotlinx.coroutines) implementation(hlaeja.kotlinx.coroutines)
implementation(hlaeja.ltd.hlaeja.library.common.messages) implementation(hlaeja.library.hlaeja.common.messages)
implementation(hlaeja.org.springframework.springboot.actuator.starter) implementation(hlaeja.library.hlaeja.jwt)
implementation(hlaeja.org.springframework.springboot.r2dbc.starter) implementation(hlaeja.springboot.starter.actuator)
implementation(hlaeja.org.springframework.springboot.webflux.starter) implementation(hlaeja.springboot.starter.r2dbc)
implementation(hlaeja.springboot.starter.webflux)
runtimeOnly(hlaeja.jjwt.impl) runtimeOnly(hlaeja.postgresql)
runtimeOnly(hlaeja.jjwt.jackson) runtimeOnly(hlaeja.postgresql.r2dbc)
runtimeOnly(hlaeja.org.postgresql)
runtimeOnly(hlaeja.org.postgresql.r2dbc)
testImplementation(hlaeja.assertj.core) testImplementation(hlaeja.assertj.core)
testImplementation(hlaeja.io.mockk) testImplementation(hlaeja.mockk)
testImplementation(hlaeja.io.projectreactor.reactor.test) testImplementation(hlaeja.projectreactor.reactor.test)
testImplementation(hlaeja.kotlin.test.junit5) testImplementation(hlaeja.kotlin.test.junit5)
testImplementation(hlaeja.kotlinx.coroutines.test) testImplementation(hlaeja.kotlinx.coroutines.test)
testImplementation(hlaeja.org.springframework.springboot.test.starter) testImplementation(hlaeja.springboot.starter.test)
testRuntimeOnly(hlaeja.org.junit.platform.launcher) testRuntimeOnly(hlaeja.junit.platform.launcher)
} }
group = "ltd.hlaeja" group = "ltd.hlaeja"
tasks { tasks.named("processResources") {
named("processResources") { dependsOn("copyCertificates")
dependsOn("copyPrivateKey")
}
register<Copy>("copyPrivateKey") {
group = "hlaeja"
from("keys/private_key.pem")
into("${layout.buildDirectory.get()}/resources/main/keys")
onlyIf { file("keys/private_key.pem").exists() }
}
} }

View File

@@ -1,4 +1,4 @@
kotlin.code.style=official kotlin.code.style=official
version=0.1.0 version=0.4.0
catalog=0.4.0 catalog=0.8.0
container.port.host=9010 container.port.host=9010

View File

@@ -5,3 +5,6 @@ Content-Type: application/json
{ {
"type": "00000000-0000-0000-0000-000000000000" "type": "00000000-0000-0000-0000-000000000000"
} }
### register device for a type
GET {{hostname}}/device-00000000-0000-0000-0000-000000000000

View File

@@ -1,24 +1,31 @@
package ltd.hlaeja.controller package ltd.hlaeja.controller
import java.util.UUID
import ltd.hlaeja.jwt.service.PrivateJwtService
import ltd.hlaeja.library.deviceRegistry.Device import ltd.hlaeja.library.deviceRegistry.Device
import ltd.hlaeja.service.DeviceService import ltd.hlaeja.service.DeviceService
import ltd.hlaeja.service.JwtService import ltd.hlaeja.util.toDeviceResponse
import org.springframework.http.HttpStatus.EXPECTATION_FAILED import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RestController import org.springframework.web.bind.annotation.RestController
import org.springframework.web.server.ResponseStatusException
@RestController @RestController
class DeviceController( class DeviceController(
private val deviceService: DeviceService, private val deviceService: DeviceService,
private val jwtService: JwtService, private val privateJwtService: PrivateJwtService,
) { ) {
@PostMapping("/device") @PostMapping("/device")
suspend fun addDevice( suspend fun addDevice(
@RequestBody request: Device.Request, @RequestBody request: Device.Request,
): Device.Identity = deviceService.addDevice(request.type) ): Device.Response = deviceService.addDevice(request.type)
.let { jwtService.makeIdentity(it.id ?: throw ResponseStatusException(EXPECTATION_FAILED)) } .toDeviceResponse(privateJwtService)
.let { Device.Identity(it) }
@GetMapping("/device-{device}")
suspend fun getDevice(
@PathVariable device: UUID,
): Device.Response = deviceService.getDevice(device)
.toDeviceResponse(privateJwtService)
} }

View File

@@ -1,11 +1,15 @@
package ltd.hlaeja.service package ltd.hlaeja.service
import io.github.oshai.kotlinlogging.KotlinLogging
import java.time.ZonedDateTime import java.time.ZonedDateTime
import java.util.UUID import java.util.UUID
import ltd.hlaeja.entity.DeviceEntity import ltd.hlaeja.entity.DeviceEntity
import ltd.hlaeja.repository.DeviceRepository import ltd.hlaeja.repository.DeviceRepository
import mu.KotlinLogging import org.springframework.dao.DataIntegrityViolationException
import org.springframework.http.HttpStatus.BAD_REQUEST
import org.springframework.http.HttpStatus.NOT_FOUND
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import org.springframework.web.server.ResponseStatusException
private val log = KotlinLogging.logger {} private val log = KotlinLogging.logger {}
@@ -16,6 +20,15 @@ class DeviceService(
suspend fun addDevice( suspend fun addDevice(
type: UUID, type: UUID,
): DeviceEntity = deviceRepository.save(DeviceEntity(null, ZonedDateTime.now(), type)) ): DeviceEntity = try {
.also { log.debug { "Added device ${it.id}" } } deviceRepository.save(DeviceEntity(null, ZonedDateTime.now(), type))
.also { log.debug { "Added device ${it.id}" } }
} catch (e: DataIntegrityViolationException) {
log.warn { e.localizedMessage }
throw ResponseStatusException(BAD_REQUEST)
}
suspend fun getDevice(device: UUID): DeviceEntity = deviceRepository.findById(device)
?.also { log.debug { "Get device ${it.id}" } }
?: throw ResponseStatusException(NOT_FOUND)
} }

View File

@@ -1,25 +0,0 @@
package ltd.hlaeja.service
import io.jsonwebtoken.Jwts
import java.security.interfaces.RSAPrivateKey
import java.util.UUID
import ltd.hlaeja.property.JwtProperty
import ltd.hlaeja.util.PrivateKeyProvider
import org.springframework.stereotype.Service
@Service
class JwtService(
jwtProperty: JwtProperty,
) {
private var privateKey: RSAPrivateKey = PrivateKeyProvider.load(jwtProperty.privateKey)
suspend fun makeIdentity(device: UUID): String {
return Jwts.builder()
.claims()
.add("device", device)
.and()
.signWith(privateKey)
.compact()
}
}

View File

@@ -1,9 +1,9 @@
package ltd.hlaeja.service package ltd.hlaeja.service
import io.github.oshai.kotlinlogging.KotlinLogging
import java.util.UUID import java.util.UUID
import ltd.hlaeja.entity.NodeEntity import ltd.hlaeja.entity.NodeEntity
import ltd.hlaeja.repository.NodeRepository import ltd.hlaeja.repository.NodeRepository
import mu.KotlinLogging
import org.springframework.http.HttpStatus.NOT_FOUND import org.springframework.http.HttpStatus.NOT_FOUND
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import org.springframework.web.server.ResponseStatusException import org.springframework.web.server.ResponseStatusException

View File

@@ -1,9 +1,9 @@
package ltd.hlaeja.service package ltd.hlaeja.service
import io.github.oshai.kotlinlogging.KotlinLogging
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import ltd.hlaeja.entity.TypeEntity import ltd.hlaeja.entity.TypeEntity
import ltd.hlaeja.repository.TypeRepository import ltd.hlaeja.repository.TypeRepository
import mu.KotlinLogging
import org.springframework.dao.DuplicateKeyException import org.springframework.dao.DuplicateKeyException
import org.springframework.http.HttpStatus import org.springframework.http.HttpStatus
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@@ -22,9 +22,9 @@ class TypeService(
entity: TypeEntity, entity: TypeEntity,
): TypeEntity = try { ): TypeEntity = try {
typeRepository.save(entity) typeRepository.save(entity)
.also { log.debug("Added new type: {}", it.id) } .also { log.debug { "Added new type: $it.id" } }
} catch (e: DuplicateKeyException) { } catch (e: DuplicateKeyException) {
log.warn(e.localizedMessage) log.warn { e.localizedMessage }
throw ResponseStatusException(HttpStatus.CONFLICT) throw ResponseStatusException(HttpStatus.CONFLICT)
} }
} }

View File

@@ -1,8 +1,11 @@
package ltd.hlaeja.util package ltd.hlaeja.util
import java.time.ZonedDateTime import java.time.ZonedDateTime
import ltd.hlaeja.entity.DeviceEntity
import ltd.hlaeja.entity.NodeEntity import ltd.hlaeja.entity.NodeEntity
import ltd.hlaeja.entity.TypeEntity import ltd.hlaeja.entity.TypeEntity
import ltd.hlaeja.jwt.service.PrivateJwtService
import ltd.hlaeja.library.deviceRegistry.Device
import ltd.hlaeja.library.deviceRegistry.Identity import ltd.hlaeja.library.deviceRegistry.Identity
import ltd.hlaeja.library.deviceRegistry.Node import ltd.hlaeja.library.deviceRegistry.Node
import ltd.hlaeja.library.deviceRegistry.Type import ltd.hlaeja.library.deviceRegistry.Type
@@ -36,3 +39,11 @@ fun NodeEntity.toIdentityResponse(): Identity.Response = Identity.Response(
id ?: throw ResponseStatusException(EXPECTATION_FAILED), id ?: throw ResponseStatusException(EXPECTATION_FAILED),
device, device,
) )
fun DeviceEntity.toDeviceResponse(
jwtService: PrivateJwtService,
): Device.Response = Device.Response(
id ?: throw ResponseStatusException(EXPECTATION_FAILED),
type,
jwtService.sign("device" to id),
)

View File

@@ -1,35 +0,0 @@
package ltd.hlaeja.util
import java.security.KeyFactory
import java.security.interfaces.RSAPrivateKey
import java.security.spec.PKCS8EncodedKeySpec
import java.util.Base64.getDecoder
import ltd.hlaeja.exception.KeyProviderException
object PrivateKeyProvider {
fun load(
pemFile: String,
): RSAPrivateKey = readPrivatePemFile(pemFile)
.let(::makePrivateKey)
private fun makePrivateKey(
privateKeyBytes: ByteArray,
): RSAPrivateKey = KeyFactory.getInstance("RSA")
.generatePrivate(PKCS8EncodedKeySpec(privateKeyBytes)) as RSAPrivateKey
private fun readPrivatePemFile(
privateKey: String,
): ByteArray = javaClass.classLoader
.getResource(privateKey)
?.readText()
?.let(::getPrivateKeyByteArray)
?: throw KeyProviderException("Could not load private key")
private fun getPrivateKeyByteArray(
keyText: String,
): ByteArray = keyText.replace(Regex("[\r\n]+"), "")
.removePrefix("-----BEGIN PRIVATE KEY-----")
.removeSuffix("-----END PRIVATE KEY-----")
.let { getDecoder().decode(it) }
}

View File

@@ -19,11 +19,6 @@
"name": "spring.application.build.os.version", "name": "spring.application.build.os.version",
"type": "java.lang.String", "type": "java.lang.String",
"description": "Application build os version." "description": "Application build os version."
},
{
"name": "jwt.private-key",
"type": "java.lang.String",
"description": "Jwt private key file."
} }
] ]
} }

View File

@@ -11,7 +11,7 @@ spring:
version: "%APP_BUILD_OS_VERSION%" version: "%APP_BUILD_OS_VERSION%"
jwt: jwt:
private-key: keys/private_key.pem private-key: cert/private_key.pem
--- ---
############################### ###############################

View File

@@ -9,11 +9,12 @@ import java.time.ZonedDateTime
import java.util.UUID import java.util.UUID
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import ltd.hlaeja.entity.DeviceEntity import ltd.hlaeja.entity.DeviceEntity
import ltd.hlaeja.jwt.service.PrivateJwtService
import ltd.hlaeja.library.deviceRegistry.Device import ltd.hlaeja.library.deviceRegistry.Device
import ltd.hlaeja.service.DeviceService import ltd.hlaeja.service.DeviceService
import ltd.hlaeja.service.JwtService
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.assertThrows
import org.springframework.web.server.ResponseStatusException import org.springframework.web.server.ResponseStatusException
@@ -27,44 +28,68 @@ class DeviceControllerTest {
} }
val deviceService: DeviceService = mockk() val deviceService: DeviceService = mockk()
val jwtService: JwtService = mockk() val privateJwtService: PrivateJwtService = mockk()
lateinit var controller: DeviceController lateinit var controller: DeviceController
@BeforeEach @BeforeEach
fun setUp() { fun setUp() {
controller = DeviceController(deviceService, jwtService) controller = DeviceController(deviceService, privateJwtService)
} }
@Test @Nested
fun `add device - success`() = runTest { inner class AddDeviceTest {
// given
val request = Device.Request(uuid)
coEvery { deviceService.addDevice(any()) } returns DeviceEntity(uuid, timestamp, uuid)
coEvery { jwtService.makeIdentity(any()) } returns PAYLOAD
// when @Test
val response = controller.addDevice(request) fun `add device - success`() = runTest {
// given
val request = Device.Request(uuid)
coEvery { deviceService.addDevice(any()) } returns DeviceEntity(uuid, timestamp, uuid)
coEvery { privateJwtService.sign(any()) } returns PAYLOAD
// then // when
coVerify(exactly = 1) { deviceService.addDevice(any()) } val response = controller.addDevice(request)
coVerify(exactly = 1) { jwtService.makeIdentity(any()) }
assertThat(response.identity).isEqualTo(PAYLOAD) // then
} coVerify(exactly = 1) { deviceService.addDevice(any()) }
coVerify(exactly = 1) { privateJwtService.sign(any()) }
@Test assertThat(response.identity).isEqualTo(PAYLOAD)
fun `add device - device service fail`() = runTest {
// given
val request = Device.Request(uuid)
coEvery { deviceService.addDevice(any()) } returns DeviceEntity(null, timestamp, uuid)
// when exception
val exception = assertThrows<ResponseStatusException> {
controller.addDevice(request)
} }
// then @Test
assertThat(exception.message).isEqualTo("417 EXPECTATION_FAILED") fun `add device - device service fail`() = runTest {
// given
val request = Device.Request(uuid)
coEvery { deviceService.addDevice(any()) } returns DeviceEntity(null, timestamp, uuid)
// when exception
val exception = assertThrows<ResponseStatusException> {
controller.addDevice(request)
}
// then
assertThat(exception.message).isEqualTo("417 EXPECTATION_FAILED")
}
}
@Nested
inner class GetDeviceTest {
@Test
fun `get device - success`() = runTest {
// given
coEvery { deviceService.getDevice(any()) } returns DeviceEntity(uuid, timestamp, uuid)
coEvery { privateJwtService.sign(any()) } returns PAYLOAD
// when
val response = controller.getDevice(uuid)
// then
coVerify(exactly = 1) { deviceService.getDevice(any()) }
coVerify(exactly = 1) { privateJwtService.sign(any()) }
assertThat(response.identity).isEqualTo(PAYLOAD)
}
} }
} }

View File

@@ -17,6 +17,8 @@ import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.springframework.web.server.ResponseStatusException
class DeviceServiceTest { class DeviceServiceTest {
companion object { companion object {
@@ -42,7 +44,7 @@ class DeviceServiceTest {
} }
@Test @Test
fun `add new type success`() = runTest { fun `add new device success`() = runTest {
// given // given
coEvery { repository.save(any()) } answers { call -> coEvery { repository.save(any()) } answers { call ->
(call.invocation.args[0] as DeviceEntity).copy(id = device) (call.invocation.args[0] as DeviceEntity).copy(id = device)
@@ -58,4 +60,37 @@ class DeviceServiceTest {
assertThat(result.timestamp.toString()).isEqualTo("2000-01-01T00:00:00.001Z[UTC]") assertThat(result.timestamp.toString()).isEqualTo("2000-01-01T00:00:00.001Z[UTC]")
assertThat(result.type).isEqualTo(type) assertThat(result.type).isEqualTo(type)
} }
@Test
fun `get device - success`() = runTest {
// given
val device = UUID.fromString("00000000-0000-0000-0000-000000000000")
val entity: DeviceEntity = mockk()
coEvery { repository.findById(any()) } returns entity
coEvery { entity.id } returns device
// when
val result = service.getDevice(type)
// then
coVerify(exactly = 1) { repository.findById(any()) }
assertThat(result.id).isEqualTo(device)
}
@Test
fun `get device - fail not found`() = runTest {
// given
val device = UUID.fromString("00000000-0000-0000-0000-000000000000")
coEvery { repository.findById(any()) } returns null
// when
val exception = assertThrows<ResponseStatusException> {
service.getDevice(device)
}
// then
assertThat(exception.message).isEqualTo("404 NOT_FOUND")
}
} }

View File

@@ -1,31 +0,0 @@
package ltd.hlaeja.service
import java.util.UUID
import kotlinx.coroutines.test.runTest
import ltd.hlaeja.property.JwtProperty
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
class JwtServiceTest {
val property: JwtProperty = JwtProperty("keys/valid-private-key.pem")
lateinit var service: JwtService
@BeforeEach
fun setUp() {
service = JwtService(property)
}
@Test
fun `should generate a JWT successfully with a valid private key`() = runTest {
// given
val deviceId = UUID.fromString("00000000-0000-0000-0000-000000000000")
// when
val jwt = service.makeIdentity(deviceId)
// then
assertThat(jwt).contains("eyJkZXZpY2UiOiIwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDAifQ")
}
}

View File

@@ -1,51 +0,0 @@
package ltd.hlaeja.util
import java.security.interfaces.RSAPrivateKey
import ltd.hlaeja.exception.KeyProviderException
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
class PrivateKeyProviderTest {
@Test
fun `load private key - success`() {
// given
val pemFilePath = "keys/valid-private-key.pem"
// when
val privateKey: RSAPrivateKey = PrivateKeyProvider.load(pemFilePath)
// then
assertThat(privateKey).isNotNull
assertThat(privateKey.algorithm).isEqualTo("RSA")
}
@Test
fun `load private key - file does not exist`() {
// given
val nonExistentPemFilePath = "keys/non-existent.pem"
// when exception
val exception = assertThrows<KeyProviderException> {
PrivateKeyProvider.load(nonExistentPemFilePath)
}
// then
assertThat(exception.message).isEqualTo("Could not load private key")
}
@Test
fun `load private key - file is invalid`() {
// given
val invalidPemFilePath = "keys/invalid-private-key.pem"
// when exception
val exception = assertThrows<IllegalArgumentException> {
PrivateKeyProvider.load(invalidPemFilePath)
}
// then
assertThat(exception.message).contains("Input byte array has wrong 4-byte ending unit")
}
}

View File

@@ -1,5 +1,5 @@
jwt: jwt:
private-key: keys/valid-private-key.pem private-key: cert/valid-private-key.pem
spring: spring:
r2dbc: r2dbc:

View File

@@ -1,28 +0,0 @@
-----BEGIN PRIVATE KEY-----
VEhJUyBJUyBKVU5LIFRISVMgSVMgSlVOSyBUSElTIElTIEpVTksgVEhJUyBJUyBK
VU5LIFRISVMgSVMgSlVOSyBUSElTIElTIEpVTksgVEhJUyBJUyBKVU5LIFRISVMg
SVMgSlVOSyBUSElTIElTIEpVTksgVEhJUyBJUyBKVU5LIFRISVMgSVMgSlVOSyBU
SElTIElTIEpVTksgVEhJUyBJUyBKVU5LIFRISVMgSVMgSlVOSyBUSElTIElTIEpV
TksgVEhJUyBJUyBKVU5LIFRISVMgSVMgSlVOSyBUSElTIElTIEpVTksgVEhJUyBJ
UyBKVU5LIFRISVMgSVMgSlVOSyBUSElTIElTIEpVTksgVEhJUyBJUyBKVU5LIFRI
SVMgSVMgSlVOSyBUSElTIElTIEpVTksgVEhJUyBJUyBKVU5LIFRISVMgSVMgSlVO
SyBUSElTIElTIEpVTksgVEhJUyBJUyBKVU5LIFRISVMgSVMgSlVOSyBUSElTIElT
IEpVTksgVEhJUyBJUyBKVU5LIFRISVMgSVMgSlVOSyBUSElTIElTIEpVTksgVEhJ
UyBJUyBKVU5LIFRISVMgSVMgSlVOSyBUSElTIElTIEpVTksgVEhJUyBJUyBKVU5L
IFRISVMgSVMgSlVOSyBUSElTIElTIEpVTksgVEhJUyBJUyBKVU5LIFRISVMgSVMg
SlVOSyBUSElTIElTIEpVTksgVEhJUyBJUyBKVU5LIFRISVMgSVMgSlVOSyBUSElT
IElTIEpVTksgVEhJUyBJUyBKVU5LIFRISVMgSVMgSlVOSyBUSElTIElTIEpVTksg
VEhJUyBJUyBKVU5LIFRISVMgSVMgSlVOSyBUSElTIElTIEpVTksgVEhJUyBJUyBK
VU5LIFRISVMgSVMgSlVOSyBUSElTIElTIEpVTksgVEhJUyBJUyBKVU5LIFRISVMg
SVMgSlVOSyBUSElTIElTIEpVTksgVEhJUyBJUyBKVU5LIFRISVMgSVMgSlVOSyBU
SElTIElTIEpVTksgVEhJUyBJUyBKVU5LIFRISVMgSVMgSlVOSyBUSElTIElTIEpV
TksgVEhJUyBJUyBKVU5LIFRISVMgSVMgSlVOSyBUSElTIElTIEpVTksgVEhJUyBJ
UyBKVU5LIFRISVMgSVMgSlVOSyBUSElTIElTIEpVTksgVEhJUyBJUyBKVU5LIFRI
SVMgSVMgSlVOSyBUSElTIElTIEpVTksgVEhJUyBJUyBKVU5LIFRISVMgSVMgSlVO
SyBUSElTIElTIEpVTksgVEhJUyBJUyBKVU5LIFRISVMgSVMgSlVOSyBUSElTIElT
IEpVTksgVEhJUyBJUyBKVU5LIFRISVMgSVMgSlVOSyBUSElTIElTIEpVTksgVEhJ
UyBJUyBKVU5LIFRISVMgSVMgSlVOSyBUSElTIElTIEpVTksgVEhJUyBJUyBKVU5L
IFRISVMgSVMgSlVOSyBUSElTIElTIEpVTksgVEhJUyBJUyBKVU5LIFRISVMgSVMg
SlVOSyBUSElTIElTIEpVTksgVEhJUyBJUyBKVU5LIFRISVMgSVMgSlVOSyBUSElT
IElTIEpVTksg==
-----END PRIVATE KEY-----