Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f52f1237a2 | |||
| 4130ba681c | |||
| df9d2c59a4 | |||
| 7d4ebab8f8 | |||
| 7184854ed4 | |||
| df8e1a7b48 | |||
| e7dbbc7a78 |
56
README.md
56
README.md
@@ -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`
|
|
||||||
|
|||||||
@@ -9,17 +9,15 @@ plugins {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(hlaeja.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.library.hlaeja.common.messages)
|
implementation(hlaeja.library.hlaeja.common.messages)
|
||||||
|
implementation(hlaeja.library.hlaeja.jwt)
|
||||||
implementation(hlaeja.springboot.starter.actuator)
|
implementation(hlaeja.springboot.starter.actuator)
|
||||||
implementation(hlaeja.springboot.starter.r2dbc)
|
implementation(hlaeja.springboot.starter.r2dbc)
|
||||||
implementation(hlaeja.springboot.starter.webflux)
|
implementation(hlaeja.springboot.starter.webflux)
|
||||||
|
|
||||||
runtimeOnly(hlaeja.jjwt.impl)
|
|
||||||
runtimeOnly(hlaeja.jjwt.jackson)
|
|
||||||
runtimeOnly(hlaeja.postgresql)
|
runtimeOnly(hlaeja.postgresql)
|
||||||
runtimeOnly(hlaeja.postgresql.r2dbc)
|
runtimeOnly(hlaeja.postgresql.r2dbc)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
version=0.2.0
|
version=0.4.0
|
||||||
catalog=0.6.0
|
catalog=0.8.0
|
||||||
container.port.host=9010
|
container.port.host=9010
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
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 ltd.hlaeja.util.toDeviceResponse
|
||||||
|
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
|
||||||
@@ -11,12 +14,18 @@ import org.springframework.web.bind.annotation.RestController
|
|||||||
@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.Response = deviceService.addDevice(request.type)
|
): Device.Response = deviceService.addDevice(request.type)
|
||||||
.toDeviceResponse(jwtService)
|
.toDeviceResponse(privateJwtService)
|
||||||
|
|
||||||
|
@GetMapping("/device-{device}")
|
||||||
|
suspend fun getDevice(
|
||||||
|
@PathVariable device: UUID,
|
||||||
|
): Device.Response = deviceService.getDevice(device)
|
||||||
|
.toDeviceResponse(privateJwtService)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
deviceRepository.save(DeviceEntity(null, ZonedDateTime.now(), type))
|
||||||
.also { log.debug { "Added device ${it.id}" } }
|
.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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import java.time.ZonedDateTime
|
|||||||
import ltd.hlaeja.entity.DeviceEntity
|
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.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
|
||||||
import ltd.hlaeja.service.JwtService
|
|
||||||
import org.springframework.http.HttpStatus.EXPECTATION_FAILED
|
import org.springframework.http.HttpStatus.EXPECTATION_FAILED
|
||||||
import org.springframework.web.server.ResponseStatusException
|
import org.springframework.web.server.ResponseStatusException
|
||||||
|
|
||||||
@@ -40,10 +40,10 @@ fun NodeEntity.toIdentityResponse(): Identity.Response = Identity.Response(
|
|||||||
device,
|
device,
|
||||||
)
|
)
|
||||||
|
|
||||||
suspend fun DeviceEntity.toDeviceResponse(
|
fun DeviceEntity.toDeviceResponse(
|
||||||
jwtService: JwtService,
|
jwtService: PrivateJwtService,
|
||||||
): Device.Response = Device.Response(
|
): Device.Response = Device.Response(
|
||||||
id ?: throw ResponseStatusException(EXPECTATION_FAILED),
|
id ?: throw ResponseStatusException(EXPECTATION_FAILED),
|
||||||
type,
|
type,
|
||||||
jwtService.makeIdentity(id),
|
jwtService.sign("device" to id),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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) }
|
|
||||||
}
|
|
||||||
@@ -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."
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,28 +28,31 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
inner class AddDeviceTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `add device - success`() = runTest {
|
fun `add device - success`() = runTest {
|
||||||
// given
|
// given
|
||||||
val request = Device.Request(uuid)
|
val request = Device.Request(uuid)
|
||||||
coEvery { deviceService.addDevice(any()) } returns DeviceEntity(uuid, timestamp, uuid)
|
coEvery { deviceService.addDevice(any()) } returns DeviceEntity(uuid, timestamp, uuid)
|
||||||
coEvery { jwtService.makeIdentity(any()) } returns PAYLOAD
|
coEvery { privateJwtService.sign(any()) } returns PAYLOAD
|
||||||
|
|
||||||
// when
|
// when
|
||||||
val response = controller.addDevice(request)
|
val response = controller.addDevice(request)
|
||||||
|
|
||||||
// then
|
// then
|
||||||
coVerify(exactly = 1) { deviceService.addDevice(any()) }
|
coVerify(exactly = 1) { deviceService.addDevice(any()) }
|
||||||
coVerify(exactly = 1) { jwtService.makeIdentity(any()) }
|
coVerify(exactly = 1) { privateJwtService.sign(any()) }
|
||||||
|
|
||||||
assertThat(response.identity).isEqualTo(PAYLOAD)
|
assertThat(response.identity).isEqualTo(PAYLOAD)
|
||||||
}
|
}
|
||||||
@@ -67,4 +71,25 @@ class DeviceControllerTest {
|
|||||||
// then
|
// then
|
||||||
assertThat(exception.message).isEqualTo("417 EXPECTATION_FAILED")
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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("cert/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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 = "cert/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 = "cert/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 = "cert/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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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-----
|
|
||||||
Reference in New Issue
Block a user