-
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
4295dbf
commit 6c8b287
Showing
55 changed files
with
2,002 additions
and
0 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 |
---|---|---|
@@ -0,0 +1,27 @@ | ||
name: Kotlin CI with Gradle | ||
|
||
on: | ||
push: | ||
branches: [ "*" ] | ||
pull_request: | ||
branches: [ "*" ] | ||
|
||
permissions: | ||
contents: read | ||
|
||
jobs: | ||
build: | ||
|
||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- uses: actions/checkout@v3 | ||
- name: Set up JDK 17 | ||
uses: actions/setup-java@v3 | ||
with: | ||
java-version: '17' | ||
distribution: 'temurin' | ||
- name: Build with Gradle | ||
uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1 | ||
with: | ||
arguments: build |
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 +1,5 @@ | ||
.idea | ||
.gradle | ||
card-service/build | ||
statistics-service/build | ||
oauth2-server/build |
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,12 @@ | ||
<component name="ProjectRunConfigurationManager"> | ||
<configuration default="false" name="idp-compose" type="docker-deploy" factoryName="docker-compose.yml" server-name="Docker"> | ||
<deployment type="docker-compose.yml"> | ||
<settings> | ||
<option name="composeProjectName" value="idp-compose" /> | ||
<option name="envFilePath" value="" /> | ||
<option name="sourceFilePath" value="docker-compose.yaml" /> | ||
</settings> | ||
</deployment> | ||
<method v="2" /> | ||
</configuration> | ||
</component> |
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,59 @@ | ||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile | ||
|
||
plugins { | ||
idea | ||
id("org.springframework.boot") version "3.1.6" | ||
id("io.spring.dependency-management") version "1.1.4" | ||
kotlin("jvm") version "1.9.20" | ||
kotlin("plugin.spring") version "1.9.20" | ||
kotlin("plugin.jpa") version "1.9.20" | ||
} | ||
|
||
group = "a.gleb" | ||
version = "1.0-RELEASE" | ||
|
||
java { | ||
sourceCompatibility = JavaVersion.VERSION_17 | ||
} | ||
|
||
repositories { | ||
mavenCentral() | ||
} | ||
|
||
val openApiVersion = "2.0.4" | ||
val loggingUtilsVersion = "3.0.5" | ||
val testContainersVersion = "1.19.3" | ||
|
||
dependencies { | ||
/* Spring Boot & Spring Cloud */ | ||
implementation("org.springframework.boot:spring-boot-starter-web") | ||
implementation("org.springframework.boot:spring-boot-starter-data-jpa") | ||
implementation("org.springframework.boot:spring-boot-starter-oauth2-resource-server") | ||
implementation("org.springframework.boot:spring-boot-starter-actuator") | ||
|
||
/* Database */ | ||
runtimeOnly("org.postgresql:postgresql") | ||
implementation("org.flywaydb:flyway-core") | ||
|
||
/* Other */ | ||
implementation("com.fasterxml.jackson.module:jackson-module-kotlin") | ||
implementation("org.jetbrains.kotlin:kotlin-reflect") | ||
implementation("io.github.microutils:kotlin-logging-jvm:$loggingUtilsVersion") | ||
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:$openApiVersion") | ||
|
||
/* Tests */ | ||
testImplementation("org.springframework.boot:spring-boot-starter-test") | ||
testImplementation("org.springframework.security:spring-security-test") | ||
testImplementation("org.testcontainers:postgresql:$testContainersVersion") | ||
} | ||
|
||
tasks.withType<KotlinCompile> { | ||
kotlinOptions { | ||
freeCompilerArgs += "-Xjsr305=strict" | ||
jvmTarget = "17" | ||
} | ||
} | ||
|
||
tasks.withType<Test> { | ||
useJUnitPlatform() | ||
} |
14 changes: 14 additions & 0 deletions
14
card-service/src/main/kotlin/a/gleb/cardservice/CardServiceApplication.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,14 @@ | ||
package a.gleb.cardservice | ||
|
||
import mu.KotlinLogging | ||
import org.springframework.boot.autoconfigure.SpringBootApplication | ||
import org.springframework.boot.runApplication | ||
|
||
val logger = KotlinLogging.logger {} | ||
|
||
@SpringBootApplication | ||
class CardServiceApplication | ||
|
||
fun main(args: Array<String>) { | ||
runApplication<CardServiceApplication>(*args) | ||
} |
34 changes: 34 additions & 0 deletions
34
card-service/src/main/kotlin/a/gleb/cardservice/configuration/OpenApiConfiguration.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,34 @@ | ||
package a.gleb.cardservice.configuration | ||
|
||
import io.swagger.v3.oas.annotations.OpenAPIDefinition | ||
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType | ||
import io.swagger.v3.oas.annotations.info.Info | ||
import io.swagger.v3.oas.annotations.security.OAuthFlow | ||
import io.swagger.v3.oas.annotations.security.OAuthFlows | ||
import io.swagger.v3.oas.annotations.security.OAuthScope | ||
import io.swagger.v3.oas.annotations.security.SecurityScheme | ||
import org.springframework.context.annotation.Configuration | ||
|
||
const val OAUTH2_SECURITY_SCHEMA = "myOauth2Security" | ||
|
||
@OpenAPIDefinition( | ||
info = Info( | ||
title = "card-manager-backend", | ||
description = "API service for manage cards entities", version = "1" | ||
) | ||
) | ||
@SecurityScheme( | ||
name = OAUTH2_SECURITY_SCHEMA, | ||
type = SecuritySchemeType.OAUTH2, | ||
flows = OAuthFlows( | ||
authorizationCode = OAuthFlow( | ||
authorizationUrl = "\${springdoc.oAuthFlow.authorizationUrl}", | ||
tokenUrl = "\${springdoc.oAuthFlow.tokenUrl}", | ||
scopes = [ | ||
OAuthScope(name = "openid", description = "openid scope") | ||
] | ||
) | ||
) | ||
) | ||
@Configuration | ||
class OpenApiConfiguration |
114 changes: 114 additions & 0 deletions
114
card-service/src/main/kotlin/a/gleb/cardservice/configuration/SecurityConfiguration.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,114 @@ | ||
package a.gleb.cardservice.configuration | ||
|
||
import a.gleb.cardservice.configuration.properties.CardServiceConfigurationProperties | ||
import org.springframework.boot.context.properties.EnableConfigurationProperties | ||
import org.springframework.context.annotation.Bean | ||
import org.springframework.context.annotation.Configuration | ||
import org.springframework.http.HttpMethod | ||
import org.springframework.security.authentication.AbstractAuthenticationToken | ||
import org.springframework.security.config.annotation.web.builders.HttpSecurity | ||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity | ||
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer | ||
import org.springframework.security.core.GrantedAuthority | ||
import org.springframework.security.core.authority.SimpleGrantedAuthority | ||
import org.springframework.security.oauth2.jwt.Jwt | ||
import org.springframework.security.web.SecurityFilterChain | ||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource | ||
|
||
private val UNPROTECTED_PATTERNS = listOf( | ||
"/actuator/**", | ||
"/swagger-ui/**", | ||
"/swagger-ui.html", | ||
"/v3/api-docs/**" | ||
) | ||
const val ROLE_CLAIM = "role" | ||
const val SCOPE_CLAIM = "scope" | ||
const val CREDENTIAL_TOKEN = "oauth2-server-token" | ||
|
||
@Configuration | ||
@EnableWebSecurity | ||
@EnableConfigurationProperties(CardServiceConfigurationProperties::class) | ||
class SecurityConfiguration( | ||
private val properties: CardServiceConfigurationProperties | ||
) { | ||
|
||
@Bean | ||
fun securityFilterChain(httpSecurity: HttpSecurity): SecurityFilterChain { | ||
return httpSecurity | ||
.csrf { it.disable() } | ||
.cors { | ||
val urlBasedCorsConfigurationSource = UrlBasedCorsConfigurationSource() | ||
urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", properties.corsConfiguration) | ||
it.configurationSource(urlBasedCorsConfigurationSource) | ||
} | ||
.oauth2ResourceServer { | ||
it.jwt { jwtConfigurer -> | ||
jwtConfigurer.jwtAuthenticationConverter { converter -> jwtAuthenticationConverter(converter) } | ||
} | ||
} | ||
.authorizeHttpRequests { customizeRequestMatcher(it) } | ||
.build() | ||
|
||
} | ||
|
||
private fun customizeRequestMatcher( | ||
matcher: AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry | ||
) { | ||
matcher.requestMatchers(*UNPROTECTED_PATTERNS.toTypedArray()).permitAll() | ||
|
||
properties.securityConstraints.forEach { securityConstraint -> | ||
|
||
val roles = securityConstraint.authRoles | ||
securityConstraint.securityCollections.forEach { securityCollection -> | ||
val patterns = securityCollection.patterns | ||
|
||
if (securityCollection.methods == null || securityCollection.methods!!.isEmpty()) { | ||
matcher.requestMatchers(*patterns.toTypedArray()).hasAnyRole(*roles.toTypedArray()) | ||
} else { | ||
securityCollection.methods!!.forEach { method -> | ||
matcher.requestMatchers(HttpMethod.valueOf(method), *patterns.toTypedArray()) | ||
.hasAnyRole(*roles.toTypedArray()) | ||
} | ||
} | ||
|
||
} | ||
} | ||
|
||
matcher.anyRequest().authenticated() | ||
} | ||
|
||
private fun jwtAuthenticationConverter(converter: Jwt): AbstractAuthenticationToken { | ||
val roles = converter.claims[ROLE_CLAIM] as List<*> | ||
val grantedAuthorities: List<GrantedAuthority> = if (roles.isNotEmpty()) { | ||
roles.asSequence() | ||
.map { "ROLE_$it" } | ||
.map { SimpleGrantedAuthority(it) } | ||
.toList() | ||
} else { | ||
(converter.claims[SCOPE_CLAIM] as List<*>).asSequence() | ||
.map { "ROLE_$it" } | ||
.map { SimpleGrantedAuthority(it) } | ||
.toList() | ||
} | ||
|
||
return AbstractAuthenticationTokenImpl(grantedAuthorities, converter) | ||
} | ||
|
||
private class AbstractAuthenticationTokenImpl( | ||
authorities: List<GrantedAuthority>, | ||
private val converter: Jwt | ||
) : AbstractAuthenticationToken(authorities) { | ||
|
||
override fun getCredentials(): Any { | ||
return CREDENTIAL_TOKEN | ||
} | ||
|
||
override fun getPrincipal(): Any { | ||
return converter | ||
} | ||
|
||
override fun isAuthenticated(): Boolean { | ||
return true | ||
} | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
.../kotlin/a/gleb/cardservice/configuration/properties/CardServiceConfigurationProperties.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,22 @@ | ||
package a.gleb.cardservice.configuration.properties | ||
|
||
import org.springframework.boot.context.properties.ConfigurationProperties | ||
import org.springframework.web.cors.CorsConfiguration | ||
|
||
@ConfigurationProperties("card-service") | ||
data class CardServiceConfigurationProperties( | ||
var corsConfiguration: Cors, | ||
var securityConstraints: List<SecurityConstraints> | ||
) | ||
|
||
class Cors: CorsConfiguration() | ||
|
||
data class SecurityConstraints( | ||
var securityCollections: List<SecurityCollection>, | ||
var authRoles: List<String> | ||
) | ||
|
||
data class SecurityCollection( | ||
var patterns: List<String>, | ||
var methods: List<String>? | ||
) |
59 changes: 59 additions & 0 deletions
59
card-service/src/main/kotlin/a/gleb/cardservice/controller/CardController.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,59 @@ | ||
package a.gleb.cardservice.controller | ||
|
||
import a.gleb.cardservice.configuration.OAUTH2_SECURITY_SCHEMA | ||
import a.gleb.cardservice.model.CardRequest | ||
import a.gleb.cardservice.model.CardResponse | ||
import a.gleb.cardservice.service.CardService | ||
import io.swagger.v3.oas.annotations.Operation | ||
import io.swagger.v3.oas.annotations.security.SecurityRequirement | ||
import io.swagger.v3.oas.annotations.tags.Tag | ||
import org.springframework.web.bind.annotation.* | ||
import java.util.* | ||
|
||
const val CARD_CONTROLLER = "card.controller" | ||
|
||
@RestController | ||
@RequestMapping("/api/v1/card") | ||
@Tag(name = CARD_CONTROLLER) | ||
class CardController( | ||
private val cardService: CardService | ||
) { | ||
|
||
@Operation( | ||
summary = "Get card by ID", | ||
security = [SecurityRequirement(name = OAUTH2_SECURITY_SCHEMA)] | ||
) | ||
@GetMapping | ||
fun get(@RequestParam id: UUID): CardResponse { | ||
return cardService.get(id) | ||
} | ||
|
||
@Operation( | ||
summary = "Create new card", | ||
security = [SecurityRequirement(name = OAUTH2_SECURITY_SCHEMA)] | ||
) | ||
@PostMapping | ||
fun create(@RequestBody request: CardRequest): CardResponse { | ||
return cardService.create(request) | ||
} | ||
|
||
@Operation( | ||
summary = "Update card", | ||
security = [SecurityRequirement(name = OAUTH2_SECURITY_SCHEMA)] | ||
) | ||
@PutMapping | ||
fun update(@RequestBody request: CardRequest): CardResponse { | ||
return cardService.update(request) | ||
} | ||
|
||
@Operation( | ||
summary = "Delete card", | ||
security = [SecurityRequirement(name = OAUTH2_SECURITY_SCHEMA)] | ||
) | ||
@DeleteMapping | ||
fun delete(@RequestParam id: UUID) { | ||
return cardService.delete(id) | ||
} | ||
|
||
|
||
} |
30 changes: 30 additions & 0 deletions
30
card-service/src/main/kotlin/a/gleb/cardservice/controller/StatisticsController.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,30 @@ | ||
package a.gleb.cardservice.controller | ||
|
||
import a.gleb.cardservice.configuration.OAUTH2_SECURITY_SCHEMA | ||
import a.gleb.cardservice.model.CardResponse | ||
import a.gleb.cardservice.service.CardService | ||
import io.swagger.v3.oas.annotations.Operation | ||
import io.swagger.v3.oas.annotations.security.SecurityRequirement | ||
import io.swagger.v3.oas.annotations.tags.Tag | ||
import org.springframework.web.bind.annotation.GetMapping | ||
import org.springframework.web.bind.annotation.RequestMapping | ||
import org.springframework.web.bind.annotation.RestController | ||
|
||
const val STATISTIC_CONTROLLER = "statistics.controller" | ||
|
||
@RestController | ||
@RequestMapping("/api/v1/statistics") | ||
@Tag(name = STATISTIC_CONTROLLER) | ||
class StatisticsController ( | ||
private val cardService: CardService | ||
){ | ||
|
||
@Operation( | ||
summary = "Load statistic by card.", | ||
security = [SecurityRequirement(name = OAUTH2_SECURITY_SCHEMA)] | ||
) | ||
@GetMapping | ||
fun statistics(): List<CardResponse> { | ||
return cardService.statistics() | ||
} | ||
} |
Oops, something went wrong.