feature Keycloak Auth
This commit is contained in:
+13
@@ -15,6 +15,7 @@ import org.springframework.beans.factory.annotation.Qualifier
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.web.bind.annotation.*
|
||||
import org.springframework.security.access.prepost.PreAuthorize
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse as SwaggerApiResponse
|
||||
|
||||
/**
|
||||
@@ -116,6 +117,7 @@ class MemberController(
|
||||
]
|
||||
)
|
||||
@GetMapping
|
||||
@PreAuthorize("hasAuthority('PERSON_READ')")
|
||||
fun getAllMembers(
|
||||
@Parameter(description = "Nur nach aktiven Mitgliedern filtern", example = "true")
|
||||
@RequestParam(defaultValue = "true") activeOnly: Boolean,
|
||||
@@ -157,6 +159,7 @@ class MemberController(
|
||||
]
|
||||
)
|
||||
@GetMapping("/{id}")
|
||||
@PreAuthorize("hasAuthority('PERSON_READ')")
|
||||
fun getMemberById(
|
||||
@Parameter(description = "Member unique identifier", example = "123e4567-e89b-12d3-a456-426614174000")
|
||||
@PathVariable id: String
|
||||
@@ -175,6 +178,7 @@ class MemberController(
|
||||
* Get member by membership number
|
||||
*/
|
||||
@GetMapping("/by-membership-number/{membershipNumber}")
|
||||
@PreAuthorize("hasAuthority('PERSON_READ')")
|
||||
fun getMemberByMembershipNumber(@PathVariable membershipNumber: String): ResponseEntity<ApiResponse<*>> {
|
||||
return try {
|
||||
val response = runBlocking { getMemberUseCase.getByMembershipNumber(membershipNumber) }
|
||||
@@ -195,6 +199,7 @@ class MemberController(
|
||||
* Get member by email
|
||||
*/
|
||||
@GetMapping("/by-email/{email}")
|
||||
@PreAuthorize("hasAuthority('PERSON_READ')")
|
||||
fun getMemberByEmail(@PathVariable email: String): ResponseEntity<ApiResponse<*>> {
|
||||
return try {
|
||||
val response = runBlocking { getMemberUseCase.getByEmail(email) }
|
||||
@@ -215,6 +220,7 @@ class MemberController(
|
||||
* Get member statistics
|
||||
*/
|
||||
@GetMapping("/stats")
|
||||
@PreAuthorize("hasAuthority('PERSON_READ')")
|
||||
fun getMemberStats(): ResponseEntity<ApiResponse<MemberStats>> {
|
||||
return try {
|
||||
val activeCount = runBlocking { memberRepository.countActive() }
|
||||
@@ -247,6 +253,7 @@ class MemberController(
|
||||
]
|
||||
)
|
||||
@PostMapping
|
||||
@PreAuthorize("hasAuthority('PERSON_CREATE')")
|
||||
fun createMember(
|
||||
@Parameter(description = "Member creation request data")
|
||||
@RequestBody createRequest: CreateMemberRequest
|
||||
@@ -277,6 +284,7 @@ class MemberController(
|
||||
* Update member
|
||||
*/
|
||||
@PutMapping("/{id}")
|
||||
@PreAuthorize("hasAuthority('PERSON_UPDATE')")
|
||||
fun updateMember(@PathVariable id: String, @RequestBody updateRequest: UpdateMemberRequest): ResponseEntity<ApiResponse<*>> {
|
||||
return try {
|
||||
val memberId = uuidFrom(id)
|
||||
@@ -320,6 +328,7 @@ class MemberController(
|
||||
* Get members with expiring memberships
|
||||
*/
|
||||
@GetMapping("/expiring-memberships")
|
||||
@PreAuthorize("hasAuthority('PERSON_READ')")
|
||||
fun getExpiringMemberships(
|
||||
@RequestParam(defaultValue = "30") daysAhead: Int
|
||||
): ResponseEntity<ApiResponse<*>> {
|
||||
@@ -343,6 +352,7 @@ class MemberController(
|
||||
* Get members by date range
|
||||
*/
|
||||
@GetMapping("/by-date-range")
|
||||
@PreAuthorize("hasAuthority('PERSON_READ')")
|
||||
fun getMembersByDateRange(
|
||||
@RequestParam startDate: String,
|
||||
@RequestParam endDate: String,
|
||||
@@ -379,6 +389,7 @@ class MemberController(
|
||||
* Validate email uniqueness
|
||||
*/
|
||||
@GetMapping("/validate/email/{email}")
|
||||
@PreAuthorize("hasAuthority('PERSON_READ')")
|
||||
fun validateEmail(
|
||||
@PathVariable email: String,
|
||||
@RequestParam(required = false) excludeMemberId: String?
|
||||
@@ -407,6 +418,7 @@ class MemberController(
|
||||
* Validate membership number uniqueness
|
||||
*/
|
||||
@GetMapping("/validate/membership-number/{membershipNumber}")
|
||||
@PreAuthorize("hasAuthority('PERSON_READ')")
|
||||
fun validateMembershipNumber(
|
||||
@PathVariable membershipNumber: String,
|
||||
@RequestParam(required = false) excludeMemberId: String?
|
||||
@@ -435,6 +447,7 @@ class MemberController(
|
||||
* Delete member
|
||||
*/
|
||||
@DeleteMapping("/{id}")
|
||||
@PreAuthorize("hasAuthority('PERSON_DELETE')")
|
||||
fun deleteMember(@PathVariable id: String): ResponseEntity<ApiResponse<String>> {
|
||||
return try {
|
||||
val memberId = uuidFrom(id)
|
||||
|
||||
@@ -35,6 +35,7 @@ dependencies {
|
||||
|
||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||
implementation("org.springframework.boot:spring-boot-starter-validation")
|
||||
implementation("org.springframework.boot:spring-boot-starter-security")
|
||||
implementation("org.springframework.boot:spring-boot-starter-actuator")
|
||||
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui")
|
||||
|
||||
|
||||
@@ -46,6 +46,8 @@ dependencies {
|
||||
implementation("org.jetbrains.kotlin:kotlin-reflect")
|
||||
// Validation for request/response validation
|
||||
implementation(libs.spring.boot.starter.validation)
|
||||
// Spring Security for method-level authorization
|
||||
implementation("org.springframework.boot:spring-boot-starter-security")
|
||||
// Actuator for health checks and metrics
|
||||
implementation(libs.spring.boot.starter.actuator)
|
||||
// === Service Discovery ===
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package at.mocode.ping.service.config
|
||||
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||
import org.springframework.security.config.http.SessionCreationPolicy
|
||||
import org.springframework.security.web.SecurityFilterChain
|
||||
|
||||
/**
|
||||
* Security configuration for the Ping Service.
|
||||
* Enables method-level security for fine-grained authorization control.
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableMethodSecurity(prePostEnabled = true)
|
||||
class SecurityConfiguration {
|
||||
|
||||
@Bean
|
||||
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
||||
return http
|
||||
.csrf { it.disable() }
|
||||
.sessionManagement { it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) }
|
||||
.authorizeHttpRequests { auth ->
|
||||
auth
|
||||
// Allow health check endpoints
|
||||
.requestMatchers("/actuator/**", "/health/**").permitAll()
|
||||
// Allow ping endpoints for monitoring (these are typically public)
|
||||
.requestMatchers("/ping/**").permitAll()
|
||||
// All other endpoints require authentication (handled by method-level security)
|
||||
.anyRequest().authenticated()
|
||||
}
|
||||
.build()
|
||||
}
|
||||
}
|
||||
+7
-1
@@ -20,7 +20,13 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers.*
|
||||
* Unit tests for PingController
|
||||
* Tests REST endpoints with mocked dependencies
|
||||
*/
|
||||
@WebMvcTest(PingController::class)
|
||||
@WebMvcTest(
|
||||
controllers = [PingController::class],
|
||||
excludeAutoConfiguration = [
|
||||
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration::class,
|
||||
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration::class
|
||||
]
|
||||
)
|
||||
@Import(PingControllerTest.TestConfig::class)
|
||||
class PingControllerTest {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user