summaryrefslogtreecommitdiff
path: root/src/main/kotlin/dev
diff options
context:
space:
mode:
authorPaul-Christian Volkmer2024-03-01 13:51:06 +0100
committerPaul-Christian Volkmer2024-03-01 13:51:06 +0100
commitfeb9f2430c0f3e73ed94f029cb6f92e7ff1eae65 (patch)
treecbbb8f405a249607ef8419db6f3d2dbe2d63ec5d /src/main/kotlin/dev
parent200c5338ea2d55c9008d646e8eb0462b71dc279f (diff)
feat: add config page for user role assignment
Diffstat (limited to 'src/main/kotlin/dev')
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/config/AppSecurityConfiguration.kt27
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/security/UserRole.kt4
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/services/UserRoleService.kt61
-rw-r--r--src/main/kotlin/dev/dnpm/etl/processor/web/ConfigController.kt44
4 files changed, 129 insertions, 7 deletions
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 900638c..ca511a7 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/config/AppSecurityConfiguration.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/config/AppSecurityConfiguration.kt
@@ -21,6 +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 org.slf4j.LoggerFactory
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.boot.context.properties.EnableConfigurationProperties
@@ -31,6 +32,8 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
import org.springframework.security.config.annotation.web.invoke
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper
+import org.springframework.security.core.session.SessionRegistry
+import org.springframework.security.core.session.SessionRegistryImpl
import org.springframework.security.core.userdetails.User
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.crypto.factory.PasswordEncoderFactories
@@ -82,7 +85,7 @@ class AppSecurityConfiguration(
@Bean
@ConditionalOnProperty(value = ["app.security.enable-oidc"], havingValue = "true")
- fun filterChainOidc(http: HttpSecurity, passwordEncoder: PasswordEncoder, userRoleRepository: UserRoleRepository): SecurityFilterChain {
+ fun filterChainOidc(http: HttpSecurity, passwordEncoder: PasswordEncoder, userRoleRepository: UserRoleRepository, sessionRegistry: SessionRegistry): SecurityFilterChain {
http {
authorizeRequests {
authorize("/configs/**", hasRole("ADMIN"))
@@ -95,7 +98,7 @@ class AppSecurityConfiguration(
authorize("*.svg", permitAll)
authorize("*.css", permitAll)
authorize("/login/**", permitAll)
- authorize(anyRequest, fullyAuthenticated)
+ authorize(anyRequest, permitAll)
}
httpBasic {
realmName = "ETL-Processor"
@@ -106,6 +109,16 @@ class AppSecurityConfiguration(
oauth2Login {
loginPage = "/login"
}
+ sessionManagement {
+ sessionConcurrency {
+ maximumSessions = 1
+ maxSessionsPreventsLogin = true
+ expiredUrl = "/login?expired"
+ }
+ sessionFixation {
+ newSession()
+ }
+ }
csrf { disable() }
}
return http.build()
@@ -151,8 +164,18 @@ class AppSecurityConfiguration(
}
@Bean
+ fun sessionRegistry(): SessionRegistry {
+ return SessionRegistryImpl()
+ }
+
+ @Bean
fun passwordEncoder(): PasswordEncoder {
return PasswordEncoderFactories.createDelegatingPasswordEncoder()
}
+ @Bean
+ @ConditionalOnProperty(value = ["app.security.enable-oidc"], havingValue = "true")
+ fun userRoleService(userRoleRepository: UserRoleRepository, sessionRegistry: SessionRegistry): UserRoleService {
+ return UserRoleService(userRoleRepository, sessionRegistry)
+ }
}
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/security/UserRole.kt b/src/main/kotlin/dev/dnpm/etl/processor/security/UserRole.kt
index 83ba662..4de31f5 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/security/UserRole.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/security/UserRole.kt
@@ -23,13 +23,13 @@ package dev.dnpm.etl.processor.security
import org.springframework.data.annotation.Id
import org.springframework.data.relational.core.mapping.Table
import org.springframework.data.repository.CrudRepository
-import java.util.Optional
+import java.util.*
@Table("user_role")
data class UserRole(
@Id val id: Long? = null,
val username: String,
- val role: Role = Role.GUEST
+ var role: Role = Role.GUEST
)
enum class Role(val value: String) {
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/services/UserRoleService.kt b/src/main/kotlin/dev/dnpm/etl/processor/services/UserRoleService.kt
new file mode 100644
index 0000000..6649f7d
--- /dev/null
+++ b/src/main/kotlin/dev/dnpm/etl/processor/services/UserRoleService.kt
@@ -0,0 +1,61 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+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<UserRole> {
+ return userRoleRepository.findAll().toList()
+ }
+
+ private fun expireSessionFor(username: String) {
+ sessionRegistry.allPrincipals
+ .filterIsInstance<OidcUser>()
+ .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 dbedee5..44ea400 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/web/ConfigController.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/web/ConfigController.kt
@@ -22,9 +22,12 @@ package dev.dnpm.etl.processor.web
import dev.dnpm.etl.processor.monitoring.ConnectionCheckService
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.services.TransformationService
+import dev.dnpm.etl.processor.services.UserRoleService
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.http.MediaType
import org.springframework.http.codec.ServerSentEvent
@@ -43,7 +46,8 @@ class ConfigController(
private val pseudonymGenerator: Generator,
private val mtbFileSender: MtbFileSender,
private val connectionCheckService: ConnectionCheckService,
- private val tokenService: TokenService?
+ private val tokenService: TokenService?,
+ private val userRoleService: UserRoleService?
) {
@GetMapping
@@ -56,10 +60,16 @@ class ConfigController(
if (tokenService != null) {
model.addAttribute("tokens", tokenService.findAll())
} else {
- model.addAttribute("tokens", listOf<Token>())
+ model.addAttribute("tokens", emptyList<Token>())
}
model.addAttribute("transformations", transformationService.getTransformations())
-
+ if (userRoleService != null) {
+ model.addAttribute("userRolesEnabled", true)
+ model.addAttribute("userRoles", userRoleService.findAll())
+ } else {
+ model.addAttribute("userRolesEnabled", false)
+ model.addAttribute("userRoles", emptyList<UserRole>())
+ }
return "configs"
}
@@ -112,6 +122,34 @@ class ConfigController(
return "configs/tokens"
}
+ @DeleteMapping(path = ["userroles/{id}"])
+ fun deleteUserRole(@PathVariable id: Long, model: Model): String {
+ if (userRoleService != null) {
+ userRoleService.deleteUserRole(id)
+
+ model.addAttribute("userRolesEnabled", true)
+ model.addAttribute("userRoles", userRoleService.findAll())
+ } else {
+ model.addAttribute("userRolesEnabled", false)
+ model.addAttribute("userRoles", emptyList<UserRole>())
+ }
+ return "configs/userroles"
+ }
+
+ @PutMapping(path = ["userroles/{id}"])
+ fun updateUserRole(@PathVariable id: Long, @ModelAttribute("role") role: Role, model: Model): String {
+ if (userRoleService != null) {
+ userRoleService.updateUserRole(id, role)
+
+ model.addAttribute("userRolesEnabled", true)
+ model.addAttribute("userRoles", userRoleService.findAll())
+ } else {
+ model.addAttribute("userRolesEnabled", false)
+ model.addAttribute("userRoles", emptyList<UserRole>())
+ }
+ return "configs/userroles"
+ }
+
@GetMapping(path = ["events"], produces = [MediaType.TEXT_EVENT_STREAM_VALUE])
fun events(): Flux<ServerSentEvent<Any>> {
return configsUpdateProducer.asFlux().map {