diff --git a/http/authentication.http b/http/authentication.http new file mode 100644 index 0000000..9ce9686 --- /dev/null +++ b/http/authentication.http @@ -0,0 +1,8 @@ +### account login +POST {{hostname}}/login +Content-Type: application/json + +{ + "username": "username", + "password": "password" +} diff --git a/src/main/kotlin/ltd/hlaeja/Application.kt b/src/main/kotlin/ltd/hlaeja/Application.kt index f9cb877..adca0c4 100644 --- a/src/main/kotlin/ltd/hlaeja/Application.kt +++ b/src/main/kotlin/ltd/hlaeja/Application.kt @@ -1,11 +1,13 @@ package ltd.hlaeja +import ltd.hlaeja.property.AccountRegistryProperty import ltd.hlaeja.property.DeviceRegistryProperty import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.boot.runApplication @EnableConfigurationProperties( + AccountRegistryProperty::class, DeviceRegistryProperty::class, ) @SpringBootApplication diff --git a/src/main/kotlin/ltd/hlaeja/controller/AuthenticationController.kt b/src/main/kotlin/ltd/hlaeja/controller/AuthenticationController.kt new file mode 100644 index 0000000..bee5d11 --- /dev/null +++ b/src/main/kotlin/ltd/hlaeja/controller/AuthenticationController.kt @@ -0,0 +1,18 @@ +package ltd.hlaeja.controller + +import ltd.hlaeja.library.accountRegistry.Authentication +import ltd.hlaeja.service.AuthenticationService +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RestController + +@RestController +class AuthenticationController( + private val authenticationService: AuthenticationService, +) { + + @PostMapping("/login") + suspend fun addDevice( + @RequestBody request: Authentication.Request, + ): Authentication.Response = authenticationService.authenticate(request) +} diff --git a/src/main/kotlin/ltd/hlaeja/property/AccountRegistryProperty.kt b/src/main/kotlin/ltd/hlaeja/property/AccountRegistryProperty.kt new file mode 100644 index 0000000..a36dd50 --- /dev/null +++ b/src/main/kotlin/ltd/hlaeja/property/AccountRegistryProperty.kt @@ -0,0 +1,8 @@ +package ltd.hlaeja.property + +import org.springframework.boot.context.properties.ConfigurationProperties + +@ConfigurationProperties(prefix = "account-registry") +data class AccountRegistryProperty( + val url: String, +) diff --git a/src/main/kotlin/ltd/hlaeja/service/AuthenticationService.kt b/src/main/kotlin/ltd/hlaeja/service/AuthenticationService.kt new file mode 100644 index 0000000..b7f73ba --- /dev/null +++ b/src/main/kotlin/ltd/hlaeja/service/AuthenticationService.kt @@ -0,0 +1,52 @@ +package ltd.hlaeja.service + +import io.github.oshai.kotlinlogging.KotlinLogging +import io.micrometer.core.instrument.Counter +import io.micrometer.core.instrument.MeterRegistry +import ltd.hlaeja.library.accountRegistry.Authentication +import ltd.hlaeja.property.AccountRegistryProperty +import ltd.hlaeja.util.accountRegistryAuthenticate +import org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR +import org.springframework.http.HttpStatus.SERVICE_UNAVAILABLE +import org.springframework.stereotype.Service +import org.springframework.web.ErrorResponseException +import org.springframework.web.reactive.function.client.WebClient +import org.springframework.web.reactive.function.client.WebClientRequestException +import org.springframework.web.reactive.function.client.WebClientResponseException +import org.springframework.web.server.ResponseStatusException + +private val log = KotlinLogging.logger {} + +@Service +class AuthenticationService( + meterRegistry: MeterRegistry, + private val webClient: WebClient, + private val property: AccountRegistryProperty, +) { + + private val accountRegistrySuccess = Counter.builder("account.registry.success") + .description("Number of successful account registry calls") + .register(meterRegistry) + + private val accountRegistryFailure = Counter.builder("account.registry.failure") + .description("Number of failed account registry calls") + .register(meterRegistry) + + suspend fun authenticate( + request: Authentication.Request, + ): Authentication.Response = try { + webClient.accountRegistryAuthenticate(request, property) + .also { accountRegistrySuccess.increment() } + } catch (e: ErrorResponseException) { + accountRegistryFailure.increment() + throw e + } catch (e: WebClientRequestException) { + accountRegistryFailure.increment() + log.error(e) { "Error device registry" } + throw ResponseStatusException(SERVICE_UNAVAILABLE) + } catch (e: WebClientResponseException) { + accountRegistryFailure.increment() + log.error(e) { "Error device registry" } + throw ResponseStatusException(INTERNAL_SERVER_ERROR) + } +} diff --git a/src/main/kotlin/ltd/hlaeja/util/WebClientCalls.kt b/src/main/kotlin/ltd/hlaeja/util/WebClientCalls.kt index 312f795..553c0bf 100644 --- a/src/main/kotlin/ltd/hlaeja/util/WebClientCalls.kt +++ b/src/main/kotlin/ltd/hlaeja/util/WebClientCalls.kt @@ -1,8 +1,13 @@ package ltd.hlaeja.util +import ltd.hlaeja.library.accountRegistry.Authentication import ltd.hlaeja.library.deviceRegistry.Device +import ltd.hlaeja.property.AccountRegistryProperty import ltd.hlaeja.property.DeviceRegistryProperty +import org.springframework.http.HttpStatus.LOCKED +import org.springframework.http.HttpStatus.NOT_FOUND import org.springframework.http.HttpStatus.REQUEST_TIMEOUT +import org.springframework.http.HttpStatus.UNAUTHORIZED import org.springframework.web.reactive.function.client.WebClient import org.springframework.web.reactive.function.client.awaitBodyOrNull import org.springframework.web.server.ResponseStatusException @@ -15,3 +20,15 @@ suspend fun WebClient.deviceRegistryCreateDevice( .bodyValue(request) .retrieve() .awaitBodyOrNull() ?: throw ResponseStatusException(REQUEST_TIMEOUT) + +suspend fun WebClient.accountRegistryAuthenticate( + request: Authentication.Request, + property: AccountRegistryProperty, +): Authentication.Response = post() + .uri("${property.url}/authenticate".also(::logCall)) + .bodyValue(request) + .retrieve() + .onStatus(LOCKED::equals) { throw ResponseStatusException(UNAUTHORIZED) } + .onStatus(UNAUTHORIZED::equals) { throw ResponseStatusException(UNAUTHORIZED) } + .onStatus(NOT_FOUND::equals) { throw ResponseStatusException(NOT_FOUND) } + .awaitBodyOrNull() ?: throw ResponseStatusException(REQUEST_TIMEOUT)