-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
5bcb2ac
commit b26c060
Showing
43 changed files
with
2,829 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,22 @@ | ||
# Aam Digital Services (aam-backend-service) | ||
|
||
Collection of aam-digital services and tools | ||
|
||
[![Maintainability](https://api.codeclimate.com/v1/badges/57213b5887a579196d6d/maintainability)](https://codeclimate.com/github/Aam-Digital/aam-services/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/57213b5887a579196d6d/test_coverage)](https://codeclimate.com/github/Aam-Digital/aam-services/test_coverage) | ||
|
||
A modularize Spring Boot application that contains API modules for [Aam Digital's case management platform](https://github.com/Aam-Digital/ndb-core). | ||
|
||
## Setup | ||
|
||
1. Create additional databases in CouchDB: `report-calculation` and `notification-webhook` (used by the Reporting Module to store details) | ||
2. Set up necessary environment variables (e.g. using an `application.env` file for docker compose): | ||
- see [example .env](./docs/examples/application.env) | ||
- CRYPTO_CONFIGURATION_SECRET: _a random secret used to encrypt data_ | ||
3. See ndb-setup for instructions to enable the backend in an overall system: [ndb-setup README](https://github.com/Aam-Digital/ndb-setup?tab=readme-ov-file#api-integrations-and-sql-reports) | ||
|
||
- see [example .env](./docs/examples/application.env) | ||
- CRYPTO_CONFIGURATION_SECRET: _a random secret used to encrypt data_ | ||
|
||
3. See ndb-setup for instructions to enable the backend in an overall system: [ndb-setup README](https://github.com/Aam-Digital/ndb-setup?tab=readme-ov-file#api-integrations-and-sql-reports) | ||
|
||
## API Modules | ||
|
||
- **[Reporting](./docs/modules/reporting.md)**: Calculate aggregated reports and run queries on all data, accessible for external services for API integrations of systems | ||
- **[Export](./docs/modules/export.md)**: Template based file export API. Uses [carbone.io](https://carbone.io) as templating engine. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
32 changes: 32 additions & 0 deletions
32
...ackend-service/src/main/kotlin/com/aamdigital/aambackendservice/auth/core/AuthProvider.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package com.aamdigital.aambackendservice.auth.core | ||
|
||
import reactor.core.publisher.Mono | ||
|
||
data class TokenResponse(val token: String) | ||
|
||
|
||
/** | ||
* Configuration data class for handling authentication. | ||
* | ||
* @property clientId The client ID used for authentication. | ||
* @property clientSecret The client secret used for authentication. | ||
* @property tokenEndpoint The endpoint URL to request the token. | ||
* @property grantType The OAuth grant type to be used. | ||
* @property scope The scope of the access request. | ||
*/ | ||
data class AuthConfig( | ||
val clientId: String, | ||
val clientSecret: String, | ||
val tokenEndpoint: String, | ||
val grantType: String, | ||
val scope: String, | ||
) | ||
|
||
/** | ||
* Interface representing an authentication provider responsible for fetching an authentication token. | ||
* | ||
* Used for fetching access tokens for third party systems. | ||
*/ | ||
interface AuthProvider { | ||
fun fetchToken(authClientConfig: AuthConfig): Mono<TokenResponse> | ||
} |
73 changes: 73 additions & 0 deletions
73
...ervice/src/main/kotlin/com/aamdigital/aambackendservice/auth/core/KeycloakAuthProvider.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package com.aamdigital.aambackendservice.auth.core | ||
|
||
import com.aamdigital.aambackendservice.error.ExternalSystemException | ||
import com.fasterxml.jackson.annotation.JsonProperty | ||
import com.fasterxml.jackson.databind.ObjectMapper | ||
import org.slf4j.LoggerFactory | ||
import org.springframework.http.MediaType | ||
import org.springframework.util.LinkedMultiValueMap | ||
import org.springframework.web.reactive.function.BodyInserters | ||
import org.springframework.web.reactive.function.client.WebClient | ||
import reactor.core.publisher.Mono | ||
|
||
data class KeycloakTokenResponse( | ||
@JsonProperty("access_token") val accessToken: String, | ||
) | ||
|
||
/** | ||
* This class is an implementation of the AuthProvider interface and is responsible for | ||
* fetching tokens from a Keycloak authentication server. Used for accessing | ||
* third party systems and services. | ||
* | ||
* Not related to authentication mechanics related to endpoints we provide to others. | ||
* | ||
* @property webClient The WebClient used for making HTTP requests. | ||
* @property objectMapper The ObjectMapper used for parsing JSON responses. | ||
*/ | ||
class KeycloakAuthProvider( | ||
val webClient: WebClient, | ||
val objectMapper: ObjectMapper, | ||
) : AuthProvider { | ||
|
||
private val logger = LoggerFactory.getLogger(javaClass) | ||
|
||
override fun fetchToken(authClientConfig: AuthConfig): Mono<TokenResponse> { | ||
val formData = LinkedMultiValueMap( | ||
mutableMapOf( | ||
"client_id" to listOf(authClientConfig.clientId), | ||
"client_secret" to listOf(authClientConfig.clientSecret), | ||
"grant_type" to listOf(authClientConfig.grantType), | ||
).also { | ||
if (authClientConfig.scope.isNotBlank()) { | ||
"scope" to listOf(authClientConfig.scope) | ||
} | ||
} | ||
) | ||
|
||
return webClient.post() | ||
.uri(authClientConfig.tokenEndpoint) | ||
.headers { | ||
it.contentType = MediaType.APPLICATION_FORM_URLENCODED | ||
} | ||
.body( | ||
BodyInserters.fromFormData( | ||
formData | ||
) | ||
).exchangeToMono { | ||
it.bodyToMono(String::class.java) | ||
}.map { | ||
parseResponse(it) | ||
}.doOnError { logger.error(it.message, it) } | ||
} | ||
|
||
private fun parseResponse(raw: String): TokenResponse { | ||
try { | ||
val keycloakTokenResponse = objectMapper.readValue(raw, KeycloakTokenResponse::class.java) | ||
return TokenResponse( | ||
token = keycloakTokenResponse.accessToken | ||
) | ||
} catch (e: Exception) { | ||
throw ExternalSystemException("Could not parse access token from KeycloakAuthProvider", e) | ||
} | ||
} | ||
} |
47 changes: 47 additions & 0 deletions
47
...end-service/src/main/kotlin/com/aamdigital/aambackendservice/auth/di/AuthConfiguration.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package com.aamdigital.aambackendservice.auth.di | ||
|
||
import com.aamdigital.aambackendservice.auth.core.AuthProvider | ||
import com.aamdigital.aambackendservice.auth.core.KeycloakAuthProvider | ||
import com.fasterxml.jackson.databind.ObjectMapper | ||
import org.springframework.beans.factory.annotation.Qualifier | ||
import org.springframework.boot.context.properties.ConfigurationProperties | ||
import org.springframework.context.annotation.Bean | ||
import org.springframework.context.annotation.Configuration | ||
import org.springframework.http.client.reactive.ReactorClientHttpConnector | ||
import org.springframework.web.reactive.function.client.WebClient | ||
import reactor.netty.http.client.HttpClient | ||
|
||
|
||
@ConfigurationProperties("aam-keycloak-client-configuration") | ||
data class KeycloakConfiguration( | ||
val maxInMemorySizeInMegaBytes: Int = 16, | ||
) | ||
|
||
@Configuration | ||
class AuthConfiguration { | ||
companion object { | ||
const val MEGA_BYTES_MULTIPLIER = 1024 * 1024 | ||
} | ||
|
||
@Bean(name = ["aam-keycloak-client"]) | ||
fun aamKeycloakWebClient( | ||
configuration: KeycloakConfiguration, | ||
): WebClient { | ||
val clientBuilder = | ||
WebClient.builder() | ||
.codecs { | ||
it.defaultCodecs() | ||
.maxInMemorySize(configuration.maxInMemorySizeInMegaBytes * MEGA_BYTES_MULTIPLIER) | ||
} | ||
|
||
return clientBuilder.clientConnector(ReactorClientHttpConnector(HttpClient.create())).build() | ||
} | ||
|
||
@Bean(name = ["aam-keycloak"]) | ||
fun aamKeycloakAuthProvider( | ||
@Qualifier("aam-keycloak-client") webClient: WebClient, | ||
objectMapper: ObjectMapper, | ||
): AuthProvider = | ||
KeycloakAuthProvider(webClient = webClient, objectMapper = objectMapper) | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
36 changes: 36 additions & 0 deletions
36
...-backend-service/src/main/kotlin/com/aamdigital/aambackendservice/domain/DomainUseCase.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package com.aamdigital.aambackendservice.domain | ||
|
||
import reactor.core.publisher.Mono | ||
|
||
|
||
interface UseCaseRequest | ||
interface UseCaseData | ||
interface UseCaseErrorCode | ||
|
||
sealed interface UseCaseOutcome<D : UseCaseData, E : UseCaseErrorCode> { | ||
data class Success<D : UseCaseData, E : UseCaseErrorCode>( | ||
val outcome: D | ||
) : UseCaseOutcome<D, E> | ||
|
||
data class Failure<D : UseCaseData, E : UseCaseErrorCode>( | ||
val errorCode: E, | ||
val errorMessage: String? = "An unspecific error occurred, while executing this use case", | ||
val cause: Throwable? = null | ||
) : UseCaseOutcome<D, E> | ||
} | ||
|
||
interface DomainUseCase<R : UseCaseRequest, D : UseCaseData, E : UseCaseErrorCode> { | ||
fun apply(request: R): Mono<UseCaseOutcome<D, E>> | ||
fun handleError(it: Throwable): Mono<UseCaseOutcome<D, E>> | ||
|
||
fun execute(request: R): Mono<UseCaseOutcome<D, E>> { | ||
return try { | ||
apply(request) | ||
.onErrorResume { | ||
handleError(it) | ||
} | ||
} catch (ex: Exception) { | ||
handleError(ex) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.