Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
afedd19143 | ||
| f0ff324cf2 | |||
| cf1b78ae0a | |||
| 69e293a25f | |||
| dec6b99281 | |||
| 93aad65385 | |||
| 3effd930ad | |||
| 97b8becd08 | |||
|
|
5e0ba7ed2a |
@@ -9,7 +9,7 @@ insert_final_newline = true
|
|||||||
max_line_length = 120
|
max_line_length = 120
|
||||||
tab_width = 4
|
tab_width = 4
|
||||||
|
|
||||||
[*.{md,sh,sql,yaml,yml}]
|
[*.{md,sh,sql,xml,xsd,yaml,yml}]
|
||||||
max_line_length = 1024
|
max_line_length = 1024
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
tab_width = 2
|
tab_width = 2
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ dependencies {
|
|||||||
implementation(hlaeja.kotlinx.coroutines)
|
implementation(hlaeja.kotlinx.coroutines)
|
||||||
implementation(hlaeja.library.common.messages)
|
implementation(hlaeja.library.common.messages)
|
||||||
implementation(hlaeja.library.jwt)
|
implementation(hlaeja.library.jwt)
|
||||||
|
implementation(hlaeja.springboot.kafka)
|
||||||
implementation(hlaeja.springboot.starter.actuator)
|
implementation(hlaeja.springboot.starter.actuator)
|
||||||
implementation(hlaeja.springboot.starter.r2dbc)
|
implementation(hlaeja.springboot.starter.r2dbc)
|
||||||
implementation(hlaeja.springboot.starter.security)
|
implementation(hlaeja.springboot.starter.security)
|
||||||
@@ -28,6 +29,7 @@ dependencies {
|
|||||||
testImplementation(hlaeja.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.springboot.kafka.test)
|
||||||
testImplementation(hlaeja.springboot.starter.test)
|
testImplementation(hlaeja.springboot.starter.test)
|
||||||
|
|
||||||
testRuntimeOnly(hlaeja.junit.platform.launcher)
|
testRuntimeOnly(hlaeja.junit.platform.launcher)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
version=0.3.0
|
version=0.4.0
|
||||||
catalog=0.11.0
|
catalog=0.12.0
|
||||||
container.port.host=9050
|
container.port.host=9050
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ Content-Type: application/json
|
|||||||
"password": "p4ssw0rd",
|
"password": "p4ssw0rd",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"roles": [
|
"roles": [
|
||||||
"ROLE_ADMIN",
|
"ADMIN",
|
||||||
"ROLE_TEST"
|
"TEST"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ Content-Type: application/json
|
|||||||
"password": "pass",
|
"password": "pass",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"roles": [
|
"roles": [
|
||||||
"ROLE_TEST"
|
"TEST"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,6 +36,6 @@ Content-Type: application/json
|
|||||||
"username": "user",
|
"username": "user",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"roles": [
|
"roles": [
|
||||||
"ROLE_TEST"
|
"TEST"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
6
sql/001-update_user_roles.sql
Normal file
6
sql/001-update_user_roles.sql
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
UPDATE public.accounts
|
||||||
|
SET
|
||||||
|
roles = REPLACE(roles, 'ROLE_', ''),
|
||||||
|
updated_at = CURRENT_TIMESTAMP
|
||||||
|
WHERE
|
||||||
|
roles LIKE '%ROLE_%';
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
insert into public.accounts (id, created_at, updated_at, enabled, username, password, roles)
|
insert into public.accounts (id, created_at, updated_at, enabled, username, password, roles)
|
||||||
values ('00000000-0000-7000-0000-000000000001'::uuid, '2000-01-01 00:00:00.000001 +00:00', '2000-01-01 00:00:01.000001 +00:00', true, 'admin', '$2a$12$KoXBoLOANMK11J4xeJHPA.Sy0FG.m8KWk7P4XFsMO.ZbFmFI2DckK', 'ROLE_ADMIN'),
|
values ('00000000-0000-7000-0000-000000000001'::uuid, '2000-01-01 00:00:00.000001 +00:00', '2000-01-01 00:00:01.000001 +00:00', true, 'admin', '$2a$12$KoXBoLOANMK11J4xeJHPA.Sy0FG.m8KWk7P4XFsMO.ZbFmFI2DckK', 'ADMIN'),
|
||||||
('00000000-0000-7000-0000-000000000002'::uuid, '2000-01-01 00:00:00.000001 +00:00', '2000-01-01 00:00:01.000001 +00:00', true, 'user', '$2a$12$KoXBoLOANMK11J4xeJHPA.Sy0FG.m8KWk7P4XFsMO.ZbFmFI2DckK', 'ROLE_USER'),
|
('00000000-0000-7000-0000-000000000002'::uuid, '2000-01-01 00:00:00.000001 +00:00', '2000-01-01 00:00:01.000001 +00:00', true, 'user', '$2a$12$KoXBoLOANMK11J4xeJHPA.Sy0FG.m8KWk7P4XFsMO.ZbFmFI2DckK', 'USER'),
|
||||||
('00000000-0000-7000-0000-000000000003'::uuid, '2000-01-01 00:00:00.000001 +00:00', '2000-01-01 00:00:01.000001 +00:00', false, 'disabled', '$2a$12$KoXBoLOANMK11J4xeJHPA.Sy0FG.m8KWk7P4XFsMO.ZbFmFI2DckK', 'ROLE_USER');
|
('00000000-0000-7000-0000-000000000002'::uuid, '2000-01-01 00:00:00.000001 +00:00', '2000-01-01 00:00:01.000001 +00:00', true, 'user', '$2a$12$KoXBoLOANMK11J4xeJHPA.Sy0FG.m8KWk7P4XFsMO.ZbFmFI2DckK', 'USER'),
|
||||||
|
('00000000-0000-7000-0000-000000000003'::uuid, '2000-01-01 00:00:00.000001 +00:00', '2000-01-01 00:00:01.000001 +00:00', false, 'disabled', '$2a$12$KoXBoLOANMK11J4xeJHPA.Sy0FG.m8KWk7P4XFsMO.ZbFmFI2DckK', 'USER');
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
package ltd.hlaeja.controller
|
package ltd.hlaeja.controller
|
||||||
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import ltd.hlaeja.validator.ValidAccount
|
|
||||||
import ltd.hlaeja.entity.AccountEntity
|
import ltd.hlaeja.entity.AccountEntity
|
||||||
import ltd.hlaeja.library.accountRegistry.Account
|
import ltd.hlaeja.library.accountRegistry.Account
|
||||||
import ltd.hlaeja.service.AccountService
|
import ltd.hlaeja.service.AccountService
|
||||||
|
import ltd.hlaeja.service.PublicEventService
|
||||||
|
import ltd.hlaeja.util.detectChanges
|
||||||
import ltd.hlaeja.util.toAccountEntity
|
import ltd.hlaeja.util.toAccountEntity
|
||||||
import ltd.hlaeja.util.toAccountResponse
|
import ltd.hlaeja.util.toAccountResponse
|
||||||
import ltd.hlaeja.util.updateAccountEntity
|
import ltd.hlaeja.util.updateAccountEntity
|
||||||
|
import ltd.hlaeja.validator.ValidAccount
|
||||||
import org.springframework.http.HttpStatus.ACCEPTED
|
import org.springframework.http.HttpStatus.ACCEPTED
|
||||||
import org.springframework.http.HttpStatus.CREATED
|
import org.springframework.http.HttpStatus.CREATED
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder
|
import org.springframework.security.crypto.password.PasswordEncoder
|
||||||
@@ -25,6 +27,7 @@ import reactor.core.publisher.Mono
|
|||||||
class AccountController(
|
class AccountController(
|
||||||
private val accountService: AccountService,
|
private val accountService: AccountService,
|
||||||
private val passwordEncoder: PasswordEncoder,
|
private val passwordEncoder: PasswordEncoder,
|
||||||
|
private val publicEventService: PublicEventService,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@GetMapping("/account-{uuid}")
|
@GetMapping("/account-{uuid}")
|
||||||
@@ -40,9 +43,13 @@ class AccountController(
|
|||||||
): Mono<Account.Response> = accountService.getUserById(uuid)
|
): Mono<Account.Response> = accountService.getUserById(uuid)
|
||||||
.map { user ->
|
.map { user ->
|
||||||
user.updateAccountEntity(request, passwordEncoder)
|
user.updateAccountEntity(request, passwordEncoder)
|
||||||
.also { if (hasChange(user, it)) throw ResponseStatusException(ACCEPTED) }
|
.let { it to it.detectChanges(user) }
|
||||||
|
.also { if (it.second.isEmpty()) throw ResponseStatusException(ACCEPTED) }
|
||||||
|
}
|
||||||
|
.flatMap { (updated: AccountEntity, changes: List<String>) ->
|
||||||
|
accountService.updateAccount(updated)
|
||||||
|
.flatMap { publicEventService.updateAccount(it, changes) }
|
||||||
}
|
}
|
||||||
.flatMap { accountService.updateAccount(it) }
|
|
||||||
.map { it.toAccountResponse() }
|
.map { it.toAccountResponse() }
|
||||||
|
|
||||||
@PostMapping("/account")
|
@PostMapping("/account")
|
||||||
@@ -51,12 +58,4 @@ class AccountController(
|
|||||||
@RequestBody @ValidAccount request: Account.Request,
|
@RequestBody @ValidAccount request: Account.Request,
|
||||||
): Mono<Account.Response> = accountService.addAccount(request.toAccountEntity(passwordEncoder))
|
): Mono<Account.Response> = accountService.addAccount(request.toAccountEntity(passwordEncoder))
|
||||||
.map { it.toAccountResponse() }
|
.map { it.toAccountResponse() }
|
||||||
|
|
||||||
private fun hasChange(
|
|
||||||
user: AccountEntity,
|
|
||||||
update: AccountEntity,
|
|
||||||
): Boolean = user.password == update.password &&
|
|
||||||
user.username == update.username &&
|
|
||||||
user.enabled == update.enabled &&
|
|
||||||
user.roles == update.roles
|
|
||||||
}
|
}
|
||||||
|
|||||||
25
src/main/kotlin/ltd/hlaeja/service/PublicEventService.kt
Normal file
25
src/main/kotlin/ltd/hlaeja/service/PublicEventService.kt
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package ltd.hlaeja.service
|
||||||
|
|
||||||
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
|
import ltd.hlaeja.entity.AccountEntity
|
||||||
|
import ltd.hlaeja.library.accountRegistry.event.AccountMessage
|
||||||
|
import org.springframework.kafka.core.KafkaTemplate
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import reactor.core.publisher.Mono
|
||||||
|
|
||||||
|
private val log = KotlinLogging.logger {}
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class PublicEventService(
|
||||||
|
private val kafkaTemplate: KafkaTemplate<String, AccountMessage>,
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun updateAccount(
|
||||||
|
account: AccountEntity,
|
||||||
|
changes: List<String>,
|
||||||
|
): Mono<AccountEntity> = Mono
|
||||||
|
.fromFuture(kafkaTemplate.send("account", "change", AccountMessage(account.id!!, changes)))
|
||||||
|
.doOnSuccess { log.trace { "Sent Kafka created event for user ${account.id}" } }
|
||||||
|
.doOnError { e -> log.error(e) { "Failed to send Kafka event" } }
|
||||||
|
.thenReturn(account)
|
||||||
|
}
|
||||||
25
src/main/kotlin/ltd/hlaeja/util/AccountUtil.kt
Normal file
25
src/main/kotlin/ltd/hlaeja/util/AccountUtil.kt
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package ltd.hlaeja.util
|
||||||
|
|
||||||
|
import ltd.hlaeja.entity.AccountEntity
|
||||||
|
import org.springframework.http.HttpStatus.ACCEPTED
|
||||||
|
import org.springframework.web.server.ResponseStatusException
|
||||||
|
|
||||||
|
fun AccountEntity.detectChanges(original: AccountEntity): List<String> {
|
||||||
|
val changes: MutableList<String> = mutableListOf()
|
||||||
|
if (original.password != password) {
|
||||||
|
changes.add("password")
|
||||||
|
}
|
||||||
|
if (original.username != username) {
|
||||||
|
changes.add("username")
|
||||||
|
}
|
||||||
|
if (original.enabled != enabled) {
|
||||||
|
changes.add("enabled")
|
||||||
|
}
|
||||||
|
if (original.roles != roles) {
|
||||||
|
changes.add("roles")
|
||||||
|
}
|
||||||
|
if (changes.isEmpty()) {
|
||||||
|
throw ResponseStatusException(ACCEPTED)
|
||||||
|
}
|
||||||
|
return changes
|
||||||
|
}
|
||||||
@@ -9,6 +9,11 @@ spring:
|
|||||||
os:
|
os:
|
||||||
name: "%APP_BUILD_OS_NAME%"
|
name: "%APP_BUILD_OS_NAME%"
|
||||||
version: "%APP_BUILD_OS_VERSION%"
|
version: "%APP_BUILD_OS_VERSION%"
|
||||||
|
r2dbc:
|
||||||
|
username: services
|
||||||
|
kafka:
|
||||||
|
producer:
|
||||||
|
value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
|
||||||
|
|
||||||
management:
|
management:
|
||||||
endpoints:
|
endpoints:
|
||||||
@@ -37,8 +42,9 @@ spring:
|
|||||||
on-profile: development
|
on-profile: development
|
||||||
r2dbc:
|
r2dbc:
|
||||||
url: r2dbc:postgresql://localhost:5432/account_registry
|
url: r2dbc:postgresql://localhost:5432/account_registry
|
||||||
username: services
|
|
||||||
password: password
|
password: password
|
||||||
|
kafka:
|
||||||
|
bootstrap-servers: localhost:9091
|
||||||
|
|
||||||
---
|
---
|
||||||
##########################
|
##########################
|
||||||
@@ -50,14 +56,17 @@ spring:
|
|||||||
on-profile: docker
|
on-profile: docker
|
||||||
r2dbc:
|
r2dbc:
|
||||||
url: r2dbc:postgresql://PostgreSQL:5432/account_registry
|
url: r2dbc:postgresql://PostgreSQL:5432/account_registry
|
||||||
username: services
|
|
||||||
password: password
|
password: password
|
||||||
|
kafka:
|
||||||
|
bootstrap-servers: kafka:9092
|
||||||
|
|
||||||
---
|
---
|
||||||
##############################
|
##############################
|
||||||
### Production environment ###
|
### Kubernetes environment ###
|
||||||
##############################
|
##############################
|
||||||
spring:
|
spring:
|
||||||
config:
|
config:
|
||||||
activate:
|
activate:
|
||||||
on-profile: production
|
on-profile: kubernetes
|
||||||
|
r2dbc:
|
||||||
|
url: r2dbc:postgresql://dependency-postgresql:5432/account_registry
|
||||||
|
|||||||
@@ -7,7 +7,10 @@
|
|||||||
<root level="INFO">
|
<root level="INFO">
|
||||||
<appender-ref ref="STDOUT"/>
|
<appender-ref ref="STDOUT"/>
|
||||||
</root>
|
</root>
|
||||||
<springProfile name="develop|docker">
|
<springProfile name="development">
|
||||||
|
<logger level="TRACE" name="ltd.hlaeja"/>
|
||||||
|
</springProfile>
|
||||||
|
<springProfile name="docker">
|
||||||
<logger level="DEBUG" name="ltd.hlaeja"/>
|
<logger level="DEBUG" name="ltd.hlaeja"/>
|
||||||
</springProfile>
|
</springProfile>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package ltd.hlaeja.controller
|
|||||||
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import ltd.hlaeja.library.accountRegistry.Account
|
import ltd.hlaeja.library.accountRegistry.Account
|
||||||
import ltd.hlaeja.test.container.PostgresContainer
|
import ltd.hlaeja.test.container.KafkaPostgresTestContainer
|
||||||
import org.assertj.core.api.SoftAssertions
|
import org.assertj.core.api.SoftAssertions
|
||||||
import org.assertj.core.api.junit.jupiter.InjectSoftAssertions
|
import org.assertj.core.api.junit.jupiter.InjectSoftAssertions
|
||||||
import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension
|
import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension
|
||||||
@@ -20,7 +20,7 @@ import org.springframework.http.HttpStatus.CONFLICT
|
|||||||
import org.springframework.test.web.reactive.server.WebTestClient
|
import org.springframework.test.web.reactive.server.WebTestClient
|
||||||
import org.springframework.test.web.reactive.server.expectBody
|
import org.springframework.test.web.reactive.server.expectBody
|
||||||
|
|
||||||
@PostgresContainer
|
@KafkaPostgresTestContainer
|
||||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||||
@ExtendWith(SoftAssertionsExtension::class)
|
@ExtendWith(SoftAssertionsExtension::class)
|
||||||
class AccountEndpoint {
|
class AccountEndpoint {
|
||||||
@@ -57,7 +57,7 @@ class AccountEndpoint {
|
|||||||
softly.assertThat(it.responseBody?.username).isEqualTo("admin")
|
softly.assertThat(it.responseBody?.username).isEqualTo("admin")
|
||||||
softly.assertThat(it.responseBody?.enabled).isTrue
|
softly.assertThat(it.responseBody?.enabled).isTrue
|
||||||
softly.assertThat(it.responseBody?.roles?.size).isEqualTo(1)
|
softly.assertThat(it.responseBody?.roles?.size).isEqualTo(1)
|
||||||
softly.assertThat(it.responseBody?.roles?.get(0)).isEqualTo("ROLE_ADMIN")
|
softly.assertThat(it.responseBody?.roles?.get(0)).isEqualTo("ADMIN")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,7 +97,7 @@ class AccountEndpoint {
|
|||||||
username = "usernameA",
|
username = "usernameA",
|
||||||
password = "abc123",
|
password = "abc123",
|
||||||
enabled = true,
|
enabled = true,
|
||||||
roles = listOf("ROLE_USER", "ROLE_TEST"),
|
roles = listOf("USER", "TEST"),
|
||||||
)
|
)
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@@ -111,8 +111,8 @@ class AccountEndpoint {
|
|||||||
softly.assertThat(it.responseBody?.username).isEqualTo("usernameA")
|
softly.assertThat(it.responseBody?.username).isEqualTo("usernameA")
|
||||||
softly.assertThat(it.responseBody?.enabled).isTrue
|
softly.assertThat(it.responseBody?.enabled).isTrue
|
||||||
softly.assertThat(it.responseBody?.roles?.size).isEqualTo(2)
|
softly.assertThat(it.responseBody?.roles?.size).isEqualTo(2)
|
||||||
softly.assertThat(it.responseBody?.roles).contains("ROLE_USER")
|
softly.assertThat(it.responseBody?.roles).contains("USER")
|
||||||
softly.assertThat(it.responseBody?.roles).contains("ROLE_TEST")
|
softly.assertThat(it.responseBody?.roles).contains("TEST")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +124,7 @@ class AccountEndpoint {
|
|||||||
username = "usernameB",
|
username = "usernameB",
|
||||||
password = null,
|
password = null,
|
||||||
enabled = false,
|
enabled = false,
|
||||||
roles = listOf("ROLE_TEST"),
|
roles = listOf("TEST"),
|
||||||
)
|
)
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@@ -138,7 +138,7 @@ class AccountEndpoint {
|
|||||||
softly.assertThat(it.responseBody?.username).isEqualTo("usernameB")
|
softly.assertThat(it.responseBody?.username).isEqualTo("usernameB")
|
||||||
softly.assertThat(it.responseBody?.enabled).isFalse
|
softly.assertThat(it.responseBody?.enabled).isFalse
|
||||||
softly.assertThat(it.responseBody?.roles?.size).isEqualTo(1)
|
softly.assertThat(it.responseBody?.roles?.size).isEqualTo(1)
|
||||||
softly.assertThat(it.responseBody?.roles).contains("ROLE_TEST")
|
softly.assertThat(it.responseBody?.roles).contains("TEST")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,7 +150,7 @@ class AccountEndpoint {
|
|||||||
username = "user",
|
username = "user",
|
||||||
password = null,
|
password = null,
|
||||||
enabled = true,
|
enabled = true,
|
||||||
roles = listOf("ROLE_USER"),
|
roles = listOf("USER"),
|
||||||
)
|
)
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@@ -168,7 +168,7 @@ class AccountEndpoint {
|
|||||||
username = "admin",
|
username = "admin",
|
||||||
password = null,
|
password = null,
|
||||||
enabled = true,
|
enabled = true,
|
||||||
roles = listOf("ROLE_USER"),
|
roles = listOf("USER"),
|
||||||
)
|
)
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@@ -186,7 +186,7 @@ class AccountEndpoint {
|
|||||||
username = "admin",
|
username = "admin",
|
||||||
password = null,
|
password = null,
|
||||||
enabled = true,
|
enabled = true,
|
||||||
roles = listOf("ROLE_USER"),
|
roles = listOf("USER"),
|
||||||
)
|
)
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@@ -202,9 +202,9 @@ class AccountEndpoint {
|
|||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@CsvSource(
|
@CsvSource(
|
||||||
"new-user, new-pass, true, 2, ROLE_USER;ROLE_TEST",
|
"new-user, new-pass, true, 2, USER;TEST",
|
||||||
"admin-user, admin-pass, false, 1, ROLE_ADMIN",
|
"admin-user, admin-pass, false, 1, ADMIN",
|
||||||
"test-user, test-pass, true, 1, ROLE_USER",
|
"test-user, test-pass, true, 1, USER",
|
||||||
)
|
)
|
||||||
fun `success added account`(
|
fun `success added account`(
|
||||||
username: String,
|
username: String,
|
||||||
@@ -242,8 +242,8 @@ class AccountEndpoint {
|
|||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@CsvSource(
|
@CsvSource(
|
||||||
"'', new-pass, ROLE_TEST",
|
"'', new-pass, TEST",
|
||||||
"new-user, '', ROLE_ADMIN",
|
"new-user, '', ADMIN",
|
||||||
"new-user, new-pass, ''",
|
"new-user, new-pass, ''",
|
||||||
)
|
)
|
||||||
fun `validation fail on empty values`(
|
fun `validation fail on empty values`(
|
||||||
@@ -276,7 +276,7 @@ class AccountEndpoint {
|
|||||||
username = "user",
|
username = "user",
|
||||||
password = "new-pass",
|
password = "new-pass",
|
||||||
enabled = true,
|
enabled = true,
|
||||||
roles = listOf("ROLE_USER", "ROLE_TEST"),
|
roles = listOf("USER", "TEST"),
|
||||||
)
|
)
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@@ -293,7 +293,7 @@ class AccountEndpoint {
|
|||||||
username = "user",
|
username = "user",
|
||||||
password = null,
|
password = null,
|
||||||
enabled = true,
|
enabled = true,
|
||||||
roles = listOf("ROLE_USER", "ROLE_TEST"),
|
roles = listOf("USER", "TEST"),
|
||||||
)
|
)
|
||||||
|
|
||||||
// when
|
// when
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package ltd.hlaeja.controller
|
package ltd.hlaeja.controller
|
||||||
|
|
||||||
import ltd.hlaeja.library.accountRegistry.Account
|
import ltd.hlaeja.library.accountRegistry.Account
|
||||||
import ltd.hlaeja.test.container.PostgresContainer
|
import ltd.hlaeja.test.container.PostgresTestContainer
|
||||||
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.Test
|
import org.junit.jupiter.api.Test
|
||||||
@@ -13,7 +13,7 @@ import org.springframework.boot.test.web.server.LocalServerPort
|
|||||||
import org.springframework.test.web.reactive.server.WebTestClient
|
import org.springframework.test.web.reactive.server.WebTestClient
|
||||||
import org.springframework.test.web.reactive.server.expectBody
|
import org.springframework.test.web.reactive.server.expectBody
|
||||||
|
|
||||||
@PostgresContainer
|
@PostgresTestContainer
|
||||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||||
class AccountsEndpoint {
|
class AccountsEndpoint {
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package ltd.hlaeja.controller
|
package ltd.hlaeja.controller
|
||||||
|
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
|
||||||
import ltd.hlaeja.library.accountRegistry.Authentication
|
import ltd.hlaeja.library.accountRegistry.Authentication
|
||||||
import ltd.hlaeja.test.compareToFile
|
import ltd.hlaeja.test.compareToFile
|
||||||
import ltd.hlaeja.test.container.PostgresContainer
|
import ltd.hlaeja.test.container.PostgresTestContainer
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.assertj.core.api.SoftAssertions
|
import org.assertj.core.api.SoftAssertions
|
||||||
import org.assertj.core.api.junit.jupiter.InjectSoftAssertions
|
import org.assertj.core.api.junit.jupiter.InjectSoftAssertions
|
||||||
import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension
|
import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension
|
||||||
@@ -17,7 +17,7 @@ import org.springframework.http.HttpStatus
|
|||||||
import org.springframework.test.web.reactive.server.WebTestClient
|
import org.springframework.test.web.reactive.server.WebTestClient
|
||||||
import org.springframework.test.web.reactive.server.expectBody
|
import org.springframework.test.web.reactive.server.expectBody
|
||||||
|
|
||||||
@PostgresContainer
|
@PostgresTestContainer
|
||||||
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
@SpringBootTest(webEnvironment = RANDOM_PORT)
|
||||||
@ExtendWith(SoftAssertionsExtension::class)
|
@ExtendWith(SoftAssertionsExtension::class)
|
||||||
class AuthenticationEndpoint {
|
class AuthenticationEndpoint {
|
||||||
|
|||||||
@@ -6,10 +6,14 @@ spring:
|
|||||||
url: r2dbc:postgresql://placeholder
|
url: r2dbc:postgresql://placeholder
|
||||||
username: placeholder
|
username: placeholder
|
||||||
password: placeholder
|
password: placeholder
|
||||||
|
kafka:
|
||||||
container:
|
consumer:
|
||||||
postgres:
|
group-id: test-group
|
||||||
version: postgres:17
|
auto-offset-reset: earliest
|
||||||
init: postgres/schema.sql
|
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
|
||||||
before: postgres/data.sql
|
value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
|
||||||
after: postgres/reset.sql
|
properties:
|
||||||
|
spring.json.trusted.packages: "*"
|
||||||
|
producer:
|
||||||
|
key-serializer: org.apache.kafka.common.serialization.StringSerializer
|
||||||
|
value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
eyJhbGciOiJSUzI1NiJ9.eyJpZCI6IjAwMDAwMDAwLTAwMDAtNzAwMC0wMDAwLTAwMDAwMDAwMDAwMSIsInVzZXJuYW1lIjoiYWRtaW4iLCJyb2xlIjoiUk9MRV9BRE1JTiJ9.D6pK86XPWcdu1imV_y_4nAM6R4WEZvJpQ7oGaPAYe0_rg3UWdmVMa8Iw7L21bRgFoyIa7FQBwb_0AXojFVdb2mdOVDeGOwxQZAx23dwqeicOGd8yUMnuBaRSnd7z4P65KPMbbf0NOTQtho0Iv5mBAwFMJoF67sw-yntfx3cD_bfrI-Rf4oZaZsVn38Y2HJBe2sO2QI4e5_7s82ikxac416OX7PcIEgaf3IeEK1fSzSjRG_dyBGT_Jq_vAzVURsSu4ep976kI-k5ZXNE9EMxKu1S-n5c5eiaqo96ObnaSl4eWFik5q8vLhNLYIYO-bQi1xlJKnStwZqtUwlR763Gd5w
|
eyJhbGciOiJSUzI1NiJ9.eyJpZCI6IjAwMDAwMDAwLTAwMDAtNzAwMC0wMDAwLTAwMDAwMDAwMDAwMSIsInVzZXJuYW1lIjoiYWRtaW4iLCJyb2xlIjoiQURNSU4ifQ.Z2mc__k9apWjJoZzJ3DWZuDiVN_jpisWtd0ecwrMnk1NrJ5Uw25pgrXPwn2aY0qYFAe0UGbE-4FhjUCxWLkknR0B-2_86IKHmN1A7z8lTqMRkK7qH-71uK0Y3o0kWKn117-FoSKDG-oefQE42NTwsSrzhiaEIzhUd0fsIyKuQCbDRol79rX5cU1HwOI8DHowpNEgvCLW1ogMWJDygq5GDgQI2HmV8vbnO1soFjKzvW3pz0sHWTimhAi76gl5mD_Lv_DdywcQWndwcGEoNj-SgHuKWktaG2_yzkoC9FQqWBgU7tukuycmLkbde_Oagydt2CAfPsBebu4Ac81UHGdUpw
|
||||||
@@ -1 +1 @@
|
|||||||
eyJhbGciOiJSUzI1NiJ9.eyJpZCI6IjAwMDAwMDAwLTAwMDAtNzAwMC0wMDAwLTAwMDAwMDAwMDAwMiIsInVzZXJuYW1lIjoidXNlciIsInJvbGUiOiJST0xFX1VTRVIifQ.GvZIq0VF9xB8UY3PUGdnc6JNeUXtv4LzHJ56hWSeqUS6BXH0M_QJ5Lu9ndh9_P85CECp3eKrW4fKymGYe-NUXCtrzhr9-SSZLF6D7GRzAJ4yZjVRCOa_dgqe1RGuIZyZpli36z4NPqeBFqtHJ3Cs5rAI-WdvxGfWPgtM2kzpSJ_0zFihp9mVcZBlWP57HlN7-oKzDJWVpO2E17fWZTy-y4pdrIUsff63c256Cy8NhiAgux9aqZTdzaqp9TsXw59bRsS5d0YH7-gJuBd4xctZwgy_41BOcRk2q-nLyLZgWJs1wmCa_zaW0Fj6fjAsYvpdPNegkpIqrHJcQpGd7nE0KQ
|
eyJhbGciOiJSUzI1NiJ9.eyJpZCI6IjAwMDAwMDAwLTAwMDAtNzAwMC0wMDAwLTAwMDAwMDAwMDAwMiIsInVzZXJuYW1lIjoidXNlciIsInJvbGUiOiJVU0VSIn0.kpmQYxhkyKsIjo9mJaysBXW0xdv8UjlmNnVsYNfBu-Tdro_0nQFVzhCcjaD6_TUhx2-3vSkvTwDtmMHsP0JC5B43K473o2zQjyHYzCNakPcNHiste9llNj12n5qUCOUMgCKb7ZztLffSIsYlSL7hyRwwmTaz73MDMYvLWAa4AgSNm8JPe3HkTkqRJ4YZ-saKO9Q0Vb9LLftB7T3b9P5kHYqzwISBsRm1rYHRRpGYs5goR2Qax1hLJBbQR4bswaeTRfl3fQ66mIr6mZqiY279wCzzueLuGyJPFzeZQYiQ2JiYRq3H2NyXCsWKCt2bK-YNwol1K3fYLPSq9kap-AGasQ
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
-- Test data
|
-- Test data
|
||||||
insert into public.accounts (id, created_at, updated_at, enabled, username, password, roles)
|
insert into public.accounts (id, created_at, updated_at, enabled, username, password, roles)
|
||||||
values ('00000000-0000-7000-0000-000000000001'::uuid, '2000-01-01 00:00:00.000001 +00:00', '2000-01-01 00:00:01.000001 +00:00', true, 'admin', '$2a$12$KoXBoLOANMK11J4xeJHPA.Sy0FG.m8KWk7P4XFsMO.ZbFmFI2DckK', 'ROLE_ADMIN'),
|
values ('00000000-0000-7000-0000-000000000001'::uuid, '2000-01-01 00:00:00.000001 +00:00', '2000-01-01 00:00:01.000001 +00:00', true, 'admin', '$2a$12$KoXBoLOANMK11J4xeJHPA.Sy0FG.m8KWk7P4XFsMO.ZbFmFI2DckK', 'ADMIN'),
|
||||||
('00000000-0000-7000-0000-000000000002'::uuid, '2000-01-01 00:00:00.000001 +00:00', '2000-01-01 00:00:01.000001 +00:00', true, 'user', '$2a$12$KoXBoLOANMK11J4xeJHPA.Sy0FG.m8KWk7P4XFsMO.ZbFmFI2DckK', 'ROLE_USER'),
|
('00000000-0000-7000-0000-000000000002'::uuid, '2000-01-01 00:00:00.000001 +00:00', '2000-01-01 00:00:01.000001 +00:00', true, 'user', '$2a$12$KoXBoLOANMK11J4xeJHPA.Sy0FG.m8KWk7P4XFsMO.ZbFmFI2DckK', 'USER'),
|
||||||
('00000000-0000-7000-0000-000000000003'::uuid, '2000-01-01 00:00:00.000001 +00:00', '2000-01-01 00:00:01.000001 +00:00', false, 'disabled', '$2a$12$KoXBoLOANMK11J4xeJHPA.Sy0FG.m8KWk7P4XFsMO.ZbFmFI2DckK', 'ROLE_USER');
|
('00000000-0000-7000-0000-000000000003'::uuid, '2000-01-01 00:00:00.000001 +00:00', '2000-01-01 00:00:01.000001 +00:00', false, 'disabled', '$2a$12$KoXBoLOANMK11J4xeJHPA.Sy0FG.m8KWk7P4XFsMO.ZbFmFI2DckK', 'USER');
|
||||||
|
|||||||
109
src/test/kotlin/ltd/hlaeja/util/AccountUtilKtTest.kt
Normal file
109
src/test/kotlin/ltd/hlaeja/util/AccountUtilKtTest.kt
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
package ltd.hlaeja.util
|
||||||
|
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.time.ZoneId
|
||||||
|
import java.time.ZonedDateTime
|
||||||
|
import java.util.UUID
|
||||||
|
import ltd.hlaeja.entity.AccountEntity
|
||||||
|
import org.assertj.core.api.SoftAssertions
|
||||||
|
import org.assertj.core.api.junit.jupiter.InjectSoftAssertions
|
||||||
|
import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension
|
||||||
|
import org.junit.jupiter.api.Assertions.assertThrows
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest
|
||||||
|
import org.junit.jupiter.params.provider.CsvSource
|
||||||
|
import org.springframework.web.server.ResponseStatusException
|
||||||
|
|
||||||
|
@ExtendWith(SoftAssertionsExtension::class)
|
||||||
|
class AccountUtilKtTest {
|
||||||
|
companion object {
|
||||||
|
val account = UUID.fromString("00000000-0000-0000-0000-000000000000")
|
||||||
|
val timestamp: ZonedDateTime = ZonedDateTime.of(LocalDateTime.of(2000, 1, 1, 0, 0, 0, 1), ZoneId.of("UTC"))
|
||||||
|
val originalUser = AccountEntity(
|
||||||
|
id = account,
|
||||||
|
createdAt = timestamp,
|
||||||
|
updatedAt = timestamp,
|
||||||
|
enabled = true,
|
||||||
|
username = "username",
|
||||||
|
password = "password",
|
||||||
|
roles = "ROLE_TEST",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectSoftAssertions
|
||||||
|
lateinit var softly: SoftAssertions
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `no change detected`() {
|
||||||
|
// given
|
||||||
|
val account = AccountEntity(
|
||||||
|
id = account,
|
||||||
|
createdAt = timestamp,
|
||||||
|
updatedAt = timestamp,
|
||||||
|
enabled = true,
|
||||||
|
username = "username",
|
||||||
|
password = "password",
|
||||||
|
roles = "ROLE_TEST",
|
||||||
|
)
|
||||||
|
|
||||||
|
// then exception
|
||||||
|
assertThrows(ResponseStatusException::class.java) {
|
||||||
|
account.detectChanges(originalUser)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource(
|
||||||
|
"false, username, password, ROLE_TEST, enabled",
|
||||||
|
"true, change, password, ROLE_TEST, username",
|
||||||
|
"true, username, change, ROLE_TEST, password",
|
||||||
|
"true, username, password, ROLE_CHANGE, roles",
|
||||||
|
)
|
||||||
|
fun `change one thing`(
|
||||||
|
enabled: Boolean,
|
||||||
|
username: String,
|
||||||
|
password: String,
|
||||||
|
roles: String,
|
||||||
|
expected: String,
|
||||||
|
) {
|
||||||
|
// given
|
||||||
|
val account = AccountEntity(
|
||||||
|
id = account,
|
||||||
|
createdAt = timestamp,
|
||||||
|
updatedAt = timestamp,
|
||||||
|
enabled = enabled,
|
||||||
|
username = username,
|
||||||
|
password = password,
|
||||||
|
roles = roles,
|
||||||
|
)
|
||||||
|
|
||||||
|
// when
|
||||||
|
val result = account.detectChanges(originalUser)
|
||||||
|
|
||||||
|
// then
|
||||||
|
softly.assertThat(result).hasSize(1)
|
||||||
|
softly.assertThat(result[0]).isEqualTo(expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `change everything`() {
|
||||||
|
// given
|
||||||
|
val account = AccountEntity(
|
||||||
|
id = account,
|
||||||
|
createdAt = timestamp,
|
||||||
|
updatedAt = timestamp,
|
||||||
|
enabled = false,
|
||||||
|
username = "change",
|
||||||
|
password = "change",
|
||||||
|
roles = "ROLE_CHANGE",
|
||||||
|
)
|
||||||
|
|
||||||
|
// when
|
||||||
|
val result = account.detectChanges(originalUser)
|
||||||
|
|
||||||
|
// then
|
||||||
|
softly.assertThat(result).hasSize(4)
|
||||||
|
softly.assertThat(result).contains("password", "username", "enabled", "roles")
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user