From 3bc148f7eaf8531e28900ca080795dc2a68753ac Mon Sep 17 00:00:00 2001
From: Paul-Christian Volkmer
Date: Tue, 7 May 2024 08:58:00 +0200
Subject: refactor: move classes into package 'security'
---
.../etl/processor/config/AppConfigurationTest.kt | 4 +-
.../processor/input/MtbFileRestControllerTest.kt | 2 +-
.../dnpm/etl/processor/web/ConfigControllerTest.kt | 4 +-
.../dnpm/etl/processor/web/LoginControllerTest.kt | 2 +-
.../dnpm/etl/processor/config/AppConfiguration.kt | 4 +-
.../processor/config/AppSecurityConfiguration.kt | 2 +-
.../dnpm/etl/processor/security/TokenService.kt | 92 +++++++++
.../dnpm/etl/processor/security/UserRoleService.kt | 58 ++++++
.../dnpm/etl/processor/services/TokenService.kt | 92 ---------
.../dnpm/etl/processor/services/UserRoleService.kt | 61 ------
.../dev/dnpm/etl/processor/web/ConfigController.kt | 6 +-
.../etl/processor/security/TokenServiceTest.kt | 154 ++++++++++++++++
.../etl/processor/security/UserRoleServiceTest.kt | 202 ++++++++++++++++++++
.../etl/processor/services/TokenServiceTest.kt | 154 ----------------
.../etl/processor/services/UserRoleServiceTest.kt | 205 ---------------------
15 files changed, 518 insertions(+), 524 deletions(-)
create mode 100644 src/main/kotlin/dev/dnpm/etl/processor/security/TokenService.kt
create mode 100644 src/main/kotlin/dev/dnpm/etl/processor/security/UserRoleService.kt
delete mode 100644 src/main/kotlin/dev/dnpm/etl/processor/services/TokenService.kt
delete mode 100644 src/main/kotlin/dev/dnpm/etl/processor/services/UserRoleService.kt
create mode 100644 src/test/kotlin/dev/dnpm/etl/processor/security/TokenServiceTest.kt
create mode 100644 src/test/kotlin/dev/dnpm/etl/processor/security/UserRoleServiceTest.kt
delete mode 100644 src/test/kotlin/dev/dnpm/etl/processor/services/TokenServiceTest.kt
delete mode 100644 src/test/kotlin/dev/dnpm/etl/processor/services/UserRoleServiceTest.kt
diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/config/AppConfigurationTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/config/AppConfigurationTest.kt
index 262aca0..c7454ed 100644
--- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/config/AppConfigurationTest.kt
+++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/config/AppConfigurationTest.kt
@@ -27,8 +27,8 @@ import dev.dnpm.etl.processor.output.RestMtbFileSender
import dev.dnpm.etl.processor.pseudonym.AnonymizingGenerator
import dev.dnpm.etl.processor.pseudonym.GpasPseudonymGenerator
import dev.dnpm.etl.processor.services.RequestProcessor
-import dev.dnpm.etl.processor.services.TokenRepository
-import dev.dnpm.etl.processor.services.TokenService
+import dev.dnpm.etl.processor.security.TokenRepository
+import dev.dnpm.etl.processor.security.TokenService
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt
index f1586d0..d8c1321 100644
--- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt
+++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt
@@ -23,7 +23,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
import de.ukw.ccc.bwhc.dto.*
import dev.dnpm.etl.processor.config.AppSecurityConfiguration
import dev.dnpm.etl.processor.services.RequestProcessor
-import dev.dnpm.etl.processor.services.TokenRepository
+import dev.dnpm.etl.processor.security.TokenRepository
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/ConfigControllerTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/ConfigControllerTest.kt
index cef63d8..9c7ae3e 100644
--- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/ConfigControllerTest.kt
+++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/ConfigControllerTest.kt
@@ -29,9 +29,9 @@ import dev.dnpm.etl.processor.output.MtbFileSender
import dev.dnpm.etl.processor.pseudonym.Generator
import dev.dnpm.etl.processor.security.Role
import dev.dnpm.etl.processor.services.RequestProcessor
-import dev.dnpm.etl.processor.services.TokenService
+import dev.dnpm.etl.processor.security.TokenService
import dev.dnpm.etl.processor.services.TransformationService
-import dev.dnpm.etl.processor.services.UserRoleService
+import dev.dnpm.etl.processor.security.UserRoleService
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested
diff --git a/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/LoginControllerTest.kt b/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/LoginControllerTest.kt
index 83cf1b8..0471543 100644
--- a/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/LoginControllerTest.kt
+++ b/src/integrationTest/kotlin/dev/dnpm/etl/processor/web/LoginControllerTest.kt
@@ -23,7 +23,7 @@ import com.gargoylesoftware.htmlunit.WebClient
import com.gargoylesoftware.htmlunit.html.HtmlPage
import dev.dnpm.etl.processor.config.AppConfiguration
import dev.dnpm.etl.processor.config.AppSecurityConfiguration
-import dev.dnpm.etl.processor.services.TokenService
+import dev.dnpm.etl.processor.security.TokenService
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfiguration.kt b/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfiguration.kt
index 6ebcadd..5fc1120 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfiguration.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppConfiguration.kt
@@ -28,8 +28,8 @@ import dev.dnpm.etl.processor.pseudonym.AnonymizingGenerator
import dev.dnpm.etl.processor.pseudonym.Generator
import dev.dnpm.etl.processor.pseudonym.GpasPseudonymGenerator
import dev.dnpm.etl.processor.pseudonym.PseudonymizeService
-import dev.dnpm.etl.processor.services.TokenRepository
-import dev.dnpm.etl.processor.services.TokenService
+import dev.dnpm.etl.processor.security.TokenRepository
+import dev.dnpm.etl.processor.security.TokenService
import dev.dnpm.etl.processor.services.Transformation
import dev.dnpm.etl.processor.services.TransformationService
import org.apache.hc.client5.http.impl.classic.HttpClients
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/config/AppSecurityConfiguration.kt b/src/main/kotlin/dev/dnpm/etl/processor/config/AppSecurityConfiguration.kt
index c377555..0da9398 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppSecurityConfiguration.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppSecurityConfiguration.kt
@@ -21,7 +21,7 @@ package dev.dnpm.etl.processor.config
import dev.dnpm.etl.processor.security.UserRole
import dev.dnpm.etl.processor.security.UserRoleRepository
-import dev.dnpm.etl.processor.services.UserRoleService
+import dev.dnpm.etl.processor.security.UserRoleService
import org.slf4j.LoggerFactory
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.boot.context.properties.EnableConfigurationProperties
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/security/TokenService.kt b/src/main/kotlin/dev/dnpm/etl/processor/security/TokenService.kt
new file mode 100644
index 0000000..44b04e8
--- /dev/null
+++ b/src/main/kotlin/dev/dnpm/etl/processor/security/TokenService.kt
@@ -0,0 +1,92 @@
+/*
+ * This file is part of ETL-Processor
+ *
+ * Copyright (c) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package dev.dnpm.etl.processor.security
+
+import jakarta.annotation.PostConstruct
+import org.springframework.data.annotation.Id
+import org.springframework.data.relational.core.mapping.Table
+import org.springframework.data.repository.CrudRepository
+import org.springframework.data.repository.findByIdOrNull
+import org.springframework.security.core.userdetails.User
+import org.springframework.security.crypto.password.PasswordEncoder
+import org.springframework.security.provisioning.InMemoryUserDetailsManager
+import java.time.Instant
+import java.util.*
+
+class TokenService(
+ private val userDetailsManager: InMemoryUserDetailsManager,
+ private val passwordEncoder: PasswordEncoder,
+ private val tokenRepository: TokenRepository
+) {
+
+ @PostConstruct
+ fun setup() {
+ tokenRepository.findAll().forEach {
+ userDetailsManager.createUser(
+ User.withUsername(it.username)
+ .password(it.password)
+ .roles("MTBFILE")
+ .build()
+ )
+ }
+ }
+
+ fun addToken(name: String): Result {
+ val username = name.lowercase().replace("""[^a-z0-9]""".toRegex(), "")
+ if (userDetailsManager.userExists(username)) {
+ return Result.failure(RuntimeException("Cannot use token name"))
+ }
+
+ val password = Base64.getEncoder().encodeToString(UUID.randomUUID().toString().encodeToByteArray())
+ val encodedPassword = passwordEncoder.encode(password).toString()
+
+ userDetailsManager.createUser(
+ User.withUsername(username)
+ .password(encodedPassword)
+ .roles("MTBFILE")
+ .build()
+ )
+
+ tokenRepository.save(Token(name = name, username = username, password = encodedPassword))
+
+ return Result.success("$username:$password")
+ }
+
+ fun deleteToken(id: Long) {
+ val token = tokenRepository.findByIdOrNull(id) ?: return
+ userDetailsManager.deleteUser(token.username)
+ tokenRepository.delete(token)
+ }
+
+ fun findAll(): List {
+ return tokenRepository.findAll().toList()
+ }
+}
+
+@Table("token")
+data class Token(
+ @Id val id: Long? = null,
+ val name: String,
+ val username: String,
+ val password: String,
+ val createdAt: Instant = Instant.now()
+)
+
+interface TokenRepository : CrudRepository
\ No newline at end of file
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/security/UserRoleService.kt b/src/main/kotlin/dev/dnpm/etl/processor/security/UserRoleService.kt
new file mode 100644
index 0000000..174f8a9
--- /dev/null
+++ b/src/main/kotlin/dev/dnpm/etl/processor/security/UserRoleService.kt
@@ -0,0 +1,58 @@
+/*
+ * This file is part of ETL-Processor
+ *
+ * Copyright (c) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package dev.dnpm.etl.processor.security
+
+import org.springframework.data.repository.findByIdOrNull
+import org.springframework.security.core.session.SessionRegistry
+import org.springframework.security.oauth2.core.oidc.user.OidcUser
+
+class UserRoleService(
+ private val userRoleRepository: UserRoleRepository,
+ private val sessionRegistry: SessionRegistry
+) {
+ fun updateUserRole(id: Long, role: Role) {
+ val userRole = userRoleRepository.findByIdOrNull(id) ?: return
+ userRole.role = role
+ userRoleRepository.save(userRole)
+ expireSessionFor(userRole.username)
+ }
+
+ fun deleteUserRole(id: Long) {
+ val userRole = userRoleRepository.findByIdOrNull(id) ?: return
+ userRoleRepository.delete(userRole)
+ expireSessionFor(userRole.username)
+ }
+
+ fun findAll(): List {
+ return userRoleRepository.findAll().toList()
+ }
+
+ private fun expireSessionFor(username: String) {
+ sessionRegistry.allPrincipals
+ .filterIsInstance()
+ .filter { it.preferredUsername == username }
+ .flatMap {
+ sessionRegistry.getAllSessions(it, true)
+ }
+ .onEach {
+ it.expireNow()
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/services/TokenService.kt b/src/main/kotlin/dev/dnpm/etl/processor/services/TokenService.kt
deleted file mode 100644
index f084408..0000000
--- a/src/main/kotlin/dev/dnpm/etl/processor/services/TokenService.kt
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * This file is part of ETL-Processor
- *
- * Copyright (c) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published
- * by the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package dev.dnpm.etl.processor.services
-
-import jakarta.annotation.PostConstruct
-import org.springframework.data.annotation.Id
-import org.springframework.data.relational.core.mapping.Table
-import org.springframework.data.repository.CrudRepository
-import org.springframework.data.repository.findByIdOrNull
-import org.springframework.security.core.userdetails.User
-import org.springframework.security.crypto.password.PasswordEncoder
-import org.springframework.security.provisioning.InMemoryUserDetailsManager
-import java.time.Instant
-import java.util.*
-
-class TokenService(
- private val userDetailsManager: InMemoryUserDetailsManager,
- private val passwordEncoder: PasswordEncoder,
- private val tokenRepository: TokenRepository
-) {
-
- @PostConstruct
- fun setup() {
- tokenRepository.findAll().forEach {
- userDetailsManager.createUser(
- User.withUsername(it.username)
- .password(it.password)
- .roles("MTBFILE")
- .build()
- )
- }
- }
-
- fun addToken(name: String): Result {
- val username = name.lowercase().replace("""[^a-z0-9]""".toRegex(), "")
- if (userDetailsManager.userExists(username)) {
- return Result.failure(RuntimeException("Cannot use token name"))
- }
-
- val password = Base64.getEncoder().encodeToString(UUID.randomUUID().toString().encodeToByteArray())
- val encodedPassword = passwordEncoder.encode(password).toString()
-
- userDetailsManager.createUser(
- User.withUsername(username)
- .password(encodedPassword)
- .roles("MTBFILE")
- .build()
- )
-
- tokenRepository.save(Token(name = name, username = username, password = encodedPassword))
-
- return Result.success("$username:$password")
- }
-
- fun deleteToken(id: Long) {
- val token = tokenRepository.findByIdOrNull(id) ?: return
- userDetailsManager.deleteUser(token.username)
- tokenRepository.delete(token)
- }
-
- fun findAll(): List {
- return tokenRepository.findAll().toList()
- }
-}
-
-@Table("token")
-data class Token(
- @Id val id: Long? = null,
- val name: String,
- val username: String,
- val password: String,
- val createdAt: Instant = Instant.now()
-)
-
-interface TokenRepository : CrudRepository
\ No newline at end of file
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/services/UserRoleService.kt b/src/main/kotlin/dev/dnpm/etl/processor/services/UserRoleService.kt
deleted file mode 100644
index 6649f7d..0000000
--- a/src/main/kotlin/dev/dnpm/etl/processor/services/UserRoleService.kt
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * This file is part of ETL-Processor
- *
- * Copyright (c) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published
- * by the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package dev.dnpm.etl.processor.services
-
-import dev.dnpm.etl.processor.security.Role
-import dev.dnpm.etl.processor.security.UserRole
-import dev.dnpm.etl.processor.security.UserRoleRepository
-import org.springframework.data.repository.findByIdOrNull
-import org.springframework.security.core.session.SessionRegistry
-import org.springframework.security.oauth2.core.oidc.user.OidcUser
-
-class UserRoleService(
- private val userRoleRepository: UserRoleRepository,
- private val sessionRegistry: SessionRegistry
-) {
- fun updateUserRole(id: Long, role: Role) {
- val userRole = userRoleRepository.findByIdOrNull(id) ?: return
- userRole.role = role
- userRoleRepository.save(userRole)
- expireSessionFor(userRole.username)
- }
-
- fun deleteUserRole(id: Long) {
- val userRole = userRoleRepository.findByIdOrNull(id) ?: return
- userRoleRepository.delete(userRole)
- expireSessionFor(userRole.username)
- }
-
- fun findAll(): List {
- return userRoleRepository.findAll().toList()
- }
-
- private fun expireSessionFor(username: String) {
- sessionRegistry.allPrincipals
- .filterIsInstance()
- .filter { it.preferredUsername == username }
- .flatMap {
- sessionRegistry.getAllSessions(it, true)
- }
- .onEach {
- it.expireNow()
- }
- }
-}
\ No newline at end of file
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/web/ConfigController.kt b/src/main/kotlin/dev/dnpm/etl/processor/web/ConfigController.kt
index 4e101eb..165535f 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/web/ConfigController.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/web/ConfigController.kt
@@ -27,10 +27,10 @@ import dev.dnpm.etl.processor.output.MtbFileSender
import dev.dnpm.etl.processor.pseudonym.Generator
import dev.dnpm.etl.processor.security.Role
import dev.dnpm.etl.processor.security.UserRole
-import dev.dnpm.etl.processor.services.Token
-import dev.dnpm.etl.processor.services.TokenService
+import dev.dnpm.etl.processor.security.Token
+import dev.dnpm.etl.processor.security.TokenService
import dev.dnpm.etl.processor.services.TransformationService
-import dev.dnpm.etl.processor.services.UserRoleService
+import dev.dnpm.etl.processor.security.UserRoleService
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.http.MediaType
import org.springframework.http.codec.ServerSentEvent
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/security/TokenServiceTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/security/TokenServiceTest.kt
new file mode 100644
index 0000000..e8f4e04
--- /dev/null
+++ b/src/test/kotlin/dev/dnpm/etl/processor/security/TokenServiceTest.kt
@@ -0,0 +1,154 @@
+/*
+ * This file is part of ETL-Processor
+ *
+ * Copyright (c) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package dev.dnpm.etl.processor.security
+
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.extension.ExtendWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyLong
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Mock
+import org.mockito.junit.jupiter.MockitoExtension
+import org.mockito.kotlin.*
+import org.springframework.security.crypto.password.PasswordEncoder
+import org.springframework.security.provisioning.InMemoryUserDetailsManager
+import java.util.*
+import java.util.function.Consumer
+
+@ExtendWith(MockitoExtension::class)
+class TokenServiceTest {
+
+ private lateinit var userDetailsManager: InMemoryUserDetailsManager
+ private lateinit var passwordEncoder: PasswordEncoder
+ private lateinit var tokenRepository: TokenRepository
+
+ private lateinit var tokenService: TokenService
+
+ @BeforeEach
+ fun setup(
+ @Mock userDetailsManager: InMemoryUserDetailsManager,
+ @Mock passwordEncoder: PasswordEncoder,
+ @Mock tokenRepository: TokenRepository
+ ) {
+ this.userDetailsManager = userDetailsManager
+ this.passwordEncoder = passwordEncoder
+ this.tokenRepository = tokenRepository
+
+ this.tokenService = TokenService(userDetailsManager, passwordEncoder, tokenRepository)
+ }
+
+ @Test
+ fun shouldEncodePasswordForNewToken() {
+ doAnswer { "{test}verysecret" }.whenever(passwordEncoder).encode(anyString())
+
+ val actual = this.tokenService.addToken("Test Token")
+
+ assertThat(actual).satisfies(
+ Consumer { assertThat(it.isSuccess).isTrue() },
+ Consumer { assertThat(it.getOrNull()).matches("testtoken:[A-Za-z0-9]{48}$") }
+ )
+ }
+
+ @Test
+ fun shouldContainAlphanumTokenUserPart() {
+ doAnswer { "{test}verysecret" }.whenever(passwordEncoder).encode(anyString())
+
+ val actual = this.tokenService.addToken("Test Token")
+
+ assertThat(actual).satisfies(
+ Consumer { assertThat(it.isSuccess).isTrue() },
+ Consumer { assertThat(it.getOrDefault("")).startsWith("testtoken:") }
+ )
+ }
+
+ @Test
+ fun shouldNotAllowSameTokenUserPartTwice() {
+ doReturn(true).whenever(userDetailsManager).userExists(anyString())
+
+ val actual = this.tokenService.addToken("Test Token")
+
+ assertThat(actual).satisfies(Consumer { assertThat(it.isFailure).isTrue() })
+ verify(tokenRepository, never()).save(any())
+ }
+
+ @Test
+ fun shouldSaveNewToken() {
+ doAnswer { "{test}verysecret" }.whenever(passwordEncoder).encode(anyString())
+
+ val actual = this.tokenService.addToken("Test Token")
+
+ val captor = ArgumentCaptor.forClass(Token::class.java)
+ verify(tokenRepository, times(1)).save(captor.capture())
+
+ assertThat(actual).satisfies(Consumer { assertThat(it.isSuccess).isTrue() })
+ assertThat(captor.value).satisfies(
+ Consumer { assertThat(it.name).isEqualTo("Test Token") },
+ Consumer { assertThat(it.username).isEqualTo("testtoken") },
+ Consumer { assertThat(it.password).isEqualTo("{test}verysecret") }
+ )
+ }
+
+ @Test
+ fun shouldDeleteExistingToken() {
+ doAnswer {
+ val id = it.arguments[0] as Long
+ Optional.of(Token(id, "Test Token", "testtoken", "{test}hsdajfgadskjhfgsdkfjg"))
+ }.whenever(tokenRepository).findById(anyLong())
+
+ this.tokenService.deleteToken(42)
+
+ val stringCaptor = ArgumentCaptor.forClass(String::class.java)
+ verify(userDetailsManager, times(1)).deleteUser(stringCaptor.capture())
+ assertThat(stringCaptor.value).isEqualTo("testtoken")
+
+ val tokenCaptor = ArgumentCaptor.forClass(Token::class.java)
+ verify(tokenRepository, times(1)).delete(tokenCaptor.capture())
+ assertThat(tokenCaptor.value.id).isEqualTo(42)
+ }
+
+ @Test
+ fun shouldReturnAllTokensFromRepository() {
+ val expected = listOf(
+ Token(1, "Test Token 1", "testtoken1", "{test}hsdajfgadskjhfgsdkfjg"),
+ Token(2, "Test Token 2", "testtoken2", "{test}asdasdasdasdasdasdasd")
+ )
+
+ doReturn(expected).whenever(tokenRepository).findAll()
+
+ assertThat(tokenService.findAll()).isEqualTo(expected)
+ }
+
+ @Test
+ fun shouldAddAllTokensFromRepositoryToUserDataManager() {
+ val expected = listOf(
+ Token(1, "Test Token 1", "testtoken1", "{test}hsdajfgadskjhfgsdkfjg"),
+ Token(2, "Test Token 2", "testtoken2", "{test}asdasdasdasdasdasdasd")
+ )
+
+ doReturn(expected).whenever(tokenRepository).findAll()
+
+ tokenService.setup()
+
+ verify(userDetailsManager, times(expected.size)).createUser(any())
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/security/UserRoleServiceTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/security/UserRoleServiceTest.kt
new file mode 100644
index 0000000..39ba7c0
--- /dev/null
+++ b/src/test/kotlin/dev/dnpm/etl/processor/security/UserRoleServiceTest.kt
@@ -0,0 +1,202 @@
+/*
+ * This file is part of ETL-Processor
+ *
+ * Copyright (c) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package dev.dnpm.etl.processor.security
+
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Nested
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.extension.ExtendWith
+import org.mockito.Mock
+import org.mockito.junit.jupiter.MockitoExtension
+import org.mockito.kotlin.*
+import org.springframework.security.core.session.SessionInformation
+import org.springframework.security.core.session.SessionRegistry
+import org.springframework.security.oauth2.core.oidc.OidcIdToken
+import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser
+import org.springframework.security.oauth2.core.oidc.user.OidcUser
+import java.time.Instant
+import java.util.*
+
+@ExtendWith(MockitoExtension::class)
+class UserRoleServiceTest {
+
+ private lateinit var userRoleRepository: UserRoleRepository
+ private lateinit var sessionRegistry: SessionRegistry
+
+ private lateinit var userRoleService: UserRoleService
+
+ @BeforeEach
+ fun setup(
+ @Mock userRoleRepository: UserRoleRepository,
+ @Mock sessionRegistry: SessionRegistry
+ ) {
+ this.userRoleRepository = userRoleRepository
+ this.sessionRegistry = sessionRegistry
+
+ this.userRoleService = UserRoleService(userRoleRepository, sessionRegistry)
+ }
+
+ @Test
+ fun shouldDelegateFindAllToRepository() {
+ userRoleService.findAll()
+
+ verify(userRoleRepository, times(1)).findAll()
+ }
+
+ @Nested
+ inner class WithExistingUserRole {
+
+ @BeforeEach
+ fun setup() {
+ doAnswer { invocation ->
+ Optional.of(
+ UserRole(invocation.getArgument(0), "patrick.tester", Role.USER)
+ )
+ }.whenever(userRoleRepository).findById(any())
+
+ doAnswer { _ ->
+ listOf(
+ dummyPrincipal()
+ )
+ }.whenever(sessionRegistry).allPrincipals
+ }
+
+ @Test
+ fun shouldUpdateUserRole() {
+ userRoleService.updateUserRole(1, Role.ADMIN)
+
+ val userRoleCaptor = argumentCaptor()
+ verify(userRoleRepository, times(1)).save(userRoleCaptor.capture())
+
+ assertThat(userRoleCaptor.firstValue.id).isEqualTo(1L)
+ assertThat(userRoleCaptor.firstValue.role).isEqualTo(Role.ADMIN)
+ }
+
+ @Test
+ fun shouldExpireSessionOnUpdate() {
+ val dummySessions = dummySessions()
+ whenever(sessionRegistry.getAllSessions(any(), any())).thenReturn(
+ dummySessions
+ )
+
+ assertThat(dummySessions.filter { it.isExpired }).hasSize(0)
+
+ userRoleService.updateUserRole(1, Role.ADMIN)
+
+ verify(sessionRegistry, times(1)).getAllSessions(any(), any())
+
+ assertThat(dummySessions.filter { it.isExpired }).hasSize(2)
+ }
+
+ @Test
+ fun shouldDeleteUserRole() {
+ userRoleService.deleteUserRole(1)
+
+ val userRoleCaptor = argumentCaptor()
+ verify(userRoleRepository, times(1)).delete(userRoleCaptor.capture())
+
+ assertThat(userRoleCaptor.firstValue.id).isEqualTo(1L)
+ assertThat(userRoleCaptor.firstValue.role).isEqualTo(Role.USER)
+ }
+
+ @Test
+ fun shouldExpireSessionOnDelete() {
+ val dummySessions = dummySessions()
+ whenever(sessionRegistry.getAllSessions(any(), any())).thenReturn(
+ dummySessions
+ )
+
+ assertThat(dummySessions.filter { it.isExpired }).hasSize(0)
+
+ userRoleService.deleteUserRole(1)
+
+ verify(sessionRegistry, times(1)).getAllSessions(any(), any())
+
+ assertThat(dummySessions.filter { it.isExpired }).hasSize(2)
+ }
+ }
+
+ @Nested
+ inner class WithoutExistingUserRole {
+
+ @BeforeEach
+ fun setup() {
+ doAnswer { _ ->
+ Optional.empty()
+ }.whenever(userRoleRepository).findById(any())
+ }
+
+ @Test
+ fun shouldNotUpdateUserRole() {
+ userRoleService.updateUserRole(1, Role.ADMIN)
+
+ verify(userRoleRepository, never()).save(any())
+ }
+
+ @Test
+ fun shouldNotExpireSessionOnUpdate() {
+ userRoleService.updateUserRole(1, Role.ADMIN)
+
+ verify(sessionRegistry, never()).getAllSessions(any(), any())
+ }
+
+ @Test
+ fun shouldNotDeleteUserRole() {
+ userRoleService.deleteUserRole(1)
+
+ verify(userRoleRepository, never()).delete(any())
+ }
+
+ @Test
+ fun shouldNotExpireSessionOnDelete() {
+ userRoleService.deleteUserRole(1)
+
+ verify(sessionRegistry, never()).getAllSessions(any(), any())
+ }
+
+ }
+
+
+ companion object {
+ private fun dummyPrincipal() = DefaultOidcUser(
+ listOf(),
+ OidcIdToken(
+ "anytokenvalue",
+ Instant.now(),
+ Instant.now().plusSeconds(10),
+ mapOf("sub" to "testsub", "preferred_username" to "patrick.tester")
+ )
+ )
+
+ private fun dummySessions() = listOf(
+ SessionInformation(
+ dummyPrincipal(),
+ "SESSIONID1",
+ Date.from(Instant.now()),
+ ),
+ SessionInformation(
+ dummyPrincipal(),
+ "SESSIONID2",
+ Date.from(Instant.now()),
+ )
+ )
+ }
+}
\ No newline at end of file
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/services/TokenServiceTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/services/TokenServiceTest.kt
deleted file mode 100644
index 1fdc3d9..0000000
--- a/src/test/kotlin/dev/dnpm/etl/processor/services/TokenServiceTest.kt
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * This file is part of ETL-Processor
- *
- * Copyright (c) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published
- * by the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package dev.dnpm.etl.processor.services
-
-import org.assertj.core.api.Assertions.assertThat
-import org.junit.jupiter.api.BeforeEach
-import org.junit.jupiter.api.Test
-import org.junit.jupiter.api.extension.ExtendWith
-import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers.anyLong
-import org.mockito.ArgumentMatchers.anyString
-import org.mockito.Mock
-import org.mockito.junit.jupiter.MockitoExtension
-import org.mockito.kotlin.*
-import org.springframework.security.crypto.password.PasswordEncoder
-import org.springframework.security.provisioning.InMemoryUserDetailsManager
-import java.util.*
-import java.util.function.Consumer
-
-@ExtendWith(MockitoExtension::class)
-class TokenServiceTest {
-
- private lateinit var userDetailsManager: InMemoryUserDetailsManager
- private lateinit var passwordEncoder: PasswordEncoder
- private lateinit var tokenRepository: TokenRepository
-
- private lateinit var tokenService: TokenService
-
- @BeforeEach
- fun setup(
- @Mock userDetailsManager: InMemoryUserDetailsManager,
- @Mock passwordEncoder: PasswordEncoder,
- @Mock tokenRepository: TokenRepository
- ) {
- this.userDetailsManager = userDetailsManager
- this.passwordEncoder = passwordEncoder
- this.tokenRepository = tokenRepository
-
- this.tokenService = TokenService(userDetailsManager, passwordEncoder, tokenRepository)
- }
-
- @Test
- fun shouldEncodePasswordForNewToken() {
- doAnswer { "{test}verysecret" }.whenever(passwordEncoder).encode(anyString())
-
- val actual = this.tokenService.addToken("Test Token")
-
- assertThat(actual).satisfies(
- Consumer { assertThat(it.isSuccess).isTrue() },
- Consumer { assertThat(it.getOrNull()).matches("testtoken:[A-Za-z0-9]{48}$") }
- )
- }
-
- @Test
- fun shouldContainAlphanumTokenUserPart() {
- doAnswer { "{test}verysecret" }.whenever(passwordEncoder).encode(anyString())
-
- val actual = this.tokenService.addToken("Test Token")
-
- assertThat(actual).satisfies(
- Consumer { assertThat(it.isSuccess).isTrue() },
- Consumer { assertThat(it.getOrDefault("")).startsWith("testtoken:") }
- )
- }
-
- @Test
- fun shouldNotAllowSameTokenUserPartTwice() {
- doReturn(true).whenever(userDetailsManager).userExists(anyString())
-
- val actual = this.tokenService.addToken("Test Token")
-
- assertThat(actual).satisfies(Consumer { assertThat(it.isFailure).isTrue() })
- verify(tokenRepository, never()).save(any())
- }
-
- @Test
- fun shouldSaveNewToken() {
- doAnswer { "{test}verysecret" }.whenever(passwordEncoder).encode(anyString())
-
- val actual = this.tokenService.addToken("Test Token")
-
- val captor = ArgumentCaptor.forClass(Token::class.java)
- verify(tokenRepository, times(1)).save(captor.capture())
-
- assertThat(actual).satisfies(Consumer { assertThat(it.isSuccess).isTrue() })
- assertThat(captor.value).satisfies(
- Consumer { assertThat(it.name).isEqualTo("Test Token") },
- Consumer { assertThat(it.username).isEqualTo("testtoken") },
- Consumer { assertThat(it.password).isEqualTo("{test}verysecret") }
- )
- }
-
- @Test
- fun shouldDeleteExistingToken() {
- doAnswer {
- val id = it.arguments[0] as Long
- Optional.of(Token(id, "Test Token", "testtoken", "{test}hsdajfgadskjhfgsdkfjg"))
- }.whenever(tokenRepository).findById(anyLong())
-
- this.tokenService.deleteToken(42)
-
- val stringCaptor = ArgumentCaptor.forClass(String::class.java)
- verify(userDetailsManager, times(1)).deleteUser(stringCaptor.capture())
- assertThat(stringCaptor.value).isEqualTo("testtoken")
-
- val tokenCaptor = ArgumentCaptor.forClass(Token::class.java)
- verify(tokenRepository, times(1)).delete(tokenCaptor.capture())
- assertThat(tokenCaptor.value.id).isEqualTo(42)
- }
-
- @Test
- fun shouldReturnAllTokensFromRepository() {
- val expected = listOf(
- Token(1, "Test Token 1", "testtoken1", "{test}hsdajfgadskjhfgsdkfjg"),
- Token(2, "Test Token 2", "testtoken2", "{test}asdasdasdasdasdasdasd")
- )
-
- doReturn(expected).whenever(tokenRepository).findAll()
-
- assertThat(tokenService.findAll()).isEqualTo(expected)
- }
-
- @Test
- fun shouldAddAllTokensFromRepositoryToUserDataManager() {
- val expected = listOf(
- Token(1, "Test Token 1", "testtoken1", "{test}hsdajfgadskjhfgsdkfjg"),
- Token(2, "Test Token 2", "testtoken2", "{test}asdasdasdasdasdasdasd")
- )
-
- doReturn(expected).whenever(tokenRepository).findAll()
-
- tokenService.setup()
-
- verify(userDetailsManager, times(expected.size)).createUser(any())
- }
-
-}
\ No newline at end of file
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/services/UserRoleServiceTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/services/UserRoleServiceTest.kt
deleted file mode 100644
index 1cc1459..0000000
--- a/src/test/kotlin/dev/dnpm/etl/processor/services/UserRoleServiceTest.kt
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * This file is part of ETL-Processor
- *
- * Copyright (c) 2024 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published
- * by the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package dev.dnpm.etl.processor.services
-
-import dev.dnpm.etl.processor.security.Role
-import dev.dnpm.etl.processor.security.UserRole
-import dev.dnpm.etl.processor.security.UserRoleRepository
-import org.assertj.core.api.Assertions.assertThat
-import org.junit.jupiter.api.BeforeEach
-import org.junit.jupiter.api.Nested
-import org.junit.jupiter.api.Test
-import org.junit.jupiter.api.extension.ExtendWith
-import org.mockito.Mock
-import org.mockito.junit.jupiter.MockitoExtension
-import org.mockito.kotlin.*
-import org.springframework.security.core.session.SessionInformation
-import org.springframework.security.core.session.SessionRegistry
-import org.springframework.security.oauth2.core.oidc.OidcIdToken
-import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser
-import org.springframework.security.oauth2.core.oidc.user.OidcUser
-import java.time.Instant
-import java.util.*
-
-@ExtendWith(MockitoExtension::class)
-class UserRoleServiceTest {
-
- private lateinit var userRoleRepository: UserRoleRepository
- private lateinit var sessionRegistry: SessionRegistry
-
- private lateinit var userRoleService: UserRoleService
-
- @BeforeEach
- fun setup(
- @Mock userRoleRepository: UserRoleRepository,
- @Mock sessionRegistry: SessionRegistry
- ) {
- this.userRoleRepository = userRoleRepository
- this.sessionRegistry = sessionRegistry
-
- this.userRoleService = UserRoleService(userRoleRepository, sessionRegistry)
- }
-
- @Test
- fun shouldDelegateFindAllToRepository() {
- userRoleService.findAll()
-
- verify(userRoleRepository, times(1)).findAll()
- }
-
- @Nested
- inner class WithExistingUserRole {
-
- @BeforeEach
- fun setup() {
- doAnswer { invocation ->
- Optional.of(
- UserRole(invocation.getArgument(0), "patrick.tester", Role.USER)
- )
- }.whenever(userRoleRepository).findById(any())
-
- doAnswer { _ ->
- listOf(
- dummyPrincipal()
- )
- }.whenever(sessionRegistry).allPrincipals
- }
-
- @Test
- fun shouldUpdateUserRole() {
- userRoleService.updateUserRole(1, Role.ADMIN)
-
- val userRoleCaptor = argumentCaptor()
- verify(userRoleRepository, times(1)).save(userRoleCaptor.capture())
-
- assertThat(userRoleCaptor.firstValue.id).isEqualTo(1L)
- assertThat(userRoleCaptor.firstValue.role).isEqualTo(Role.ADMIN)
- }
-
- @Test
- fun shouldExpireSessionOnUpdate() {
- val dummySessions = dummySessions()
- whenever(sessionRegistry.getAllSessions(any(), any())).thenReturn(
- dummySessions
- )
-
- assertThat(dummySessions.filter { it.isExpired }).hasSize(0)
-
- userRoleService.updateUserRole(1, Role.ADMIN)
-
- verify(sessionRegistry, times(1)).getAllSessions(any(), any())
-
- assertThat(dummySessions.filter { it.isExpired }).hasSize(2)
- }
-
- @Test
- fun shouldDeleteUserRole() {
- userRoleService.deleteUserRole(1)
-
- val userRoleCaptor = argumentCaptor()
- verify(userRoleRepository, times(1)).delete(userRoleCaptor.capture())
-
- assertThat(userRoleCaptor.firstValue.id).isEqualTo(1L)
- assertThat(userRoleCaptor.firstValue.role).isEqualTo(Role.USER)
- }
-
- @Test
- fun shouldExpireSessionOnDelete() {
- val dummySessions = dummySessions()
- whenever(sessionRegistry.getAllSessions(any(), any())).thenReturn(
- dummySessions
- )
-
- assertThat(dummySessions.filter { it.isExpired }).hasSize(0)
-
- userRoleService.deleteUserRole(1)
-
- verify(sessionRegistry, times(1)).getAllSessions(any(), any())
-
- assertThat(dummySessions.filter { it.isExpired }).hasSize(2)
- }
- }
-
- @Nested
- inner class WithoutExistingUserRole {
-
- @BeforeEach
- fun setup() {
- doAnswer { _ ->
- Optional.empty()
- }.whenever(userRoleRepository).findById(any())
- }
-
- @Test
- fun shouldNotUpdateUserRole() {
- userRoleService.updateUserRole(1, Role.ADMIN)
-
- verify(userRoleRepository, never()).save(any())
- }
-
- @Test
- fun shouldNotExpireSessionOnUpdate() {
- userRoleService.updateUserRole(1, Role.ADMIN)
-
- verify(sessionRegistry, never()).getAllSessions(any(), any())
- }
-
- @Test
- fun shouldNotDeleteUserRole() {
- userRoleService.deleteUserRole(1)
-
- verify(userRoleRepository, never()).delete(any())
- }
-
- @Test
- fun shouldNotExpireSessionOnDelete() {
- userRoleService.deleteUserRole(1)
-
- verify(sessionRegistry, never()).getAllSessions(any(), any())
- }
-
- }
-
-
- companion object {
- private fun dummyPrincipal() = DefaultOidcUser(
- listOf(),
- OidcIdToken(
- "anytokenvalue",
- Instant.now(),
- Instant.now().plusSeconds(10),
- mapOf("sub" to "testsub", "preferred_username" to "patrick.tester")
- )
- )
-
- private fun dummySessions() = listOf(
- SessionInformation(
- dummyPrincipal(),
- "SESSIONID1",
- Date.from(Instant.now()),
- ),
- SessionInformation(
- dummyPrincipal(),
- "SESSIONID2",
- Date.from(Instant.now()),
- )
- )
- }
-}
\ No newline at end of file
--
cgit v1.2.3