client worker & auth

This commit is contained in:
Jari 2025-03-19 15:24:00 +01:00
parent 9421c9f593
commit 36da51d9a6
17 changed files with 83 additions and 24 deletions

View File

@ -5,6 +5,5 @@ enum class EUserRoles(val role: String) {
PSICO("PSICO"), PSICO("PSICO"),
ALL("ALL"), ALL("ALL"),
ADMIN("ADMIN"), ADMIN("ADMIN"),
BASE("BASE"), BASE("BASE")
EMPTY("")
} }

View File

@ -1,8 +1,15 @@
package org.octopus.lorca_core.common.models package org.octopus.lorca_core.common.models
import java.util.*
data class Client( data class Client(
var id: Long?, var id: Long?,
var name: String, var name: String,
var surname: String, var surname: String,
var dni: String,
val phoneNumber: String,
val expNumber: String,
val userNumber: String,
val birthDate: Date,
val deactivated: Boolean val deactivated: Boolean
) )

View File

@ -47,7 +47,7 @@ class JwtAuthenticationFilter(
val roles = jwtService.getRoles(jwt) val roles = jwtService.getRoles(jwt)
if (jwtService.isTokenValid(jwt, userDetails) && roles.isNotEmpty()) { if (jwtService.isTokenValid(jwt, userDetails)) {
val authToken = UsernamePasswordAuthenticationToken( val authToken = UsernamePasswordAuthenticationToken(
userDetails, userDetails,
null, null,

View File

@ -1,8 +1,10 @@
package org.octopus.lorca_core.config.security package org.octopus.lorca_core.config.security
import org.octopus.lorca_core.common.enums.EUserRoles
import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Configuration
import org.springframework.security.authentication.AuthenticationProvider import org.springframework.security.authentication.AuthenticationProvider
import org.springframework.security.authorization.AuthorizationDecision
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity 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.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
@ -26,8 +28,12 @@ class SecurityConfig(
.authorizeHttpRequests { auth -> .authorizeHttpRequests { auth ->
auth auth
.requestMatchers("/auth/**").permitAll() .requestMatchers("/auth/**").permitAll()
.requestMatchers("/workers/register/**").hasRole("EMPTY") .requestMatchers("/workers/register/**").access { authentication, _ ->
.anyRequest().authenticated() AuthorizationDecision(
authentication.get().isAuthenticated && authentication.get().authorities.isEmpty()
)
}
.anyRequest().hasAnyRole(*EUserRoles.entries.map { role -> role.role }.toTypedArray())
} }
.sessionManagement { session -> .sessionManagement { session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS) session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)

View File

@ -17,6 +17,18 @@ class ClientServiceImpl(val repo: ClientRepository) : ClientService {
} }
override fun createClient(clientDto: ClientDto): Client { override fun createClient(clientDto: ClientDto): Client {
return repo.createClient(Client(null, clientDto.name, clientDto.surname, clientDto.deactivated)) return repo.createClient(
Client(
id = 0L,
name = clientDto.name,
surname = clientDto.surname,
dni = clientDto.dni,
birthDate = clientDto.dateOfBirth,
userNumber = clientDto.numUser,
expNumber = clientDto.numExp,
phoneNumber = clientDto.phone,
deactivated = clientDto.deactivated ?: false
)
)
} }
} }

View File

@ -78,7 +78,7 @@ class JwtServiceImpl(
extractAllClaims(token)["roles"]?.let { roles -> extractAllClaims(token)["roles"]?.let { roles ->
return (roles as List<String>).map { EUserRoles.valueOf(it) } return (roles as List<String>).map { EUserRoles.valueOf(it) }
} }
return listOf(EUserRoles.EMPTY) return listOf()
} }
private fun isTokenExpired(token: String): Boolean { private fun isTokenExpired(token: String): Boolean {

View File

@ -16,7 +16,7 @@ import org.springframework.web.bind.annotation.RestController
class AdminController(val adminService: AdminService) { class AdminController(val adminService: AdminService) {
@PutMapping("/modifyRoles") @PutMapping("/modifyRoles")
fun addRoles(@Valid @RequestBody addRoleDto: AddRoleDto): WebResponse<Nothing> { fun addRoles(@Valid @RequestBody addRoleDto: AddRoleDto): WebResponse<Unit> {
adminService.modifyRoles(addRoleDto.username, addRoleDto.roles) adminService.modifyRoles(addRoleDto.username, addRoleDto.roles)
return WebResponse.ok() return WebResponse.ok()
} }

View File

@ -20,7 +20,7 @@ class AuthController(
) { ) {
@PostMapping("/register") @PostMapping("/register")
fun register(@Valid @RequestBody registerUserDto: UserAuthDto): WebResponse<Nothing> { fun register(@Valid @RequestBody registerUserDto: UserAuthDto): WebResponse<Unit> {
authenticationService.register(registerUserDto) authenticationService.register(registerUserDto)
return WebResponse.ok() return WebResponse.ok()
} }

View File

@ -40,7 +40,7 @@ class WorkerController(
} }
@PostMapping("/test") @PostMapping("/test")
fun createTestData(): WebResponse<Nothing> { fun createTestData(): WebResponse<Unit> {
// workerService.createTestData() // workerService.createTestData()
return WebResponse.ok() return WebResponse.ok()
} }

View File

@ -21,7 +21,7 @@ class BaseAdvice {
fun handleLorcaBusinessException( fun handleLorcaBusinessException(
ex: LorcaException, ex: LorcaException,
request: WebRequest request: WebRequest
): WebResponse<Nothing> { ): WebResponse<Unit> {
return WebResponse.ko( return WebResponse.ko(
deductStatus(ex.ex), deductStatus(ex.ex),
ex.message ex.message
@ -42,7 +42,7 @@ class BaseAdvice {
fun handleNumberFormatException( fun handleNumberFormatException(
ex: NumberFormatException, ex: NumberFormatException,
request: WebRequest request: WebRequest
): WebResponse<Nothing> { ): WebResponse<Unit> {
return WebResponse.ko( return WebResponse.ko(
HttpStatus.BAD_REQUEST, HttpStatus.BAD_REQUEST,
"${HttpStatus.BAD_REQUEST.reasonPhrase}: ${ex.message}" "${HttpStatus.BAD_REQUEST.reasonPhrase}: ${ex.message}"
@ -54,7 +54,7 @@ class BaseAdvice {
fun handleNoResourceFoundException( fun handleNoResourceFoundException(
ex: NoResourceFoundException, ex: NoResourceFoundException,
request: WebRequest request: WebRequest
): WebResponse<Nothing> { ): WebResponse<Unit> {
return WebResponse.ko( return WebResponse.ko(
HttpStatus.NOT_FOUND, HttpStatus.NOT_FOUND,
"${HttpStatus.NOT_FOUND.reasonPhrase}: ${(request as ServletWebRequest).request.requestURI}" "${HttpStatus.NOT_FOUND.reasonPhrase}: ${(request as ServletWebRequest).request.requestURI}"
@ -66,7 +66,7 @@ class BaseAdvice {
fun handleHttpRequestMethodNotSupportedException( fun handleHttpRequestMethodNotSupportedException(
ex: HttpRequestMethodNotSupportedException, ex: HttpRequestMethodNotSupportedException,
request: WebRequest request: WebRequest
): WebResponse<Nothing> { ): WebResponse<Unit> {
return WebResponse.ko( return WebResponse.ko(
HttpStatus.METHOD_NOT_ALLOWED, HttpStatus.METHOD_NOT_ALLOWED,
"${HttpStatus.METHOD_NOT_ALLOWED.reasonPhrase}: ${(request as ServletWebRequest).request.requestURI}" "${HttpStatus.METHOD_NOT_ALLOWED.reasonPhrase}: ${(request as ServletWebRequest).request.requestURI}"
@ -79,7 +79,7 @@ class BaseAdvice {
fun handleMethodArgumentNotValidException( fun handleMethodArgumentNotValidException(
ex: MethodArgumentNotValidException, ex: MethodArgumentNotValidException,
request: WebRequest request: WebRequest
): WebResponse<Nothing> { ): WebResponse<Unit> {
val errors = val errors =
ex.bindingResult.fieldErrors.map { ex.bindingResult.fieldErrors.map {
"${it.field} - ${it.defaultMessage}" "${it.field} - ${it.defaultMessage}"
@ -95,7 +95,7 @@ class BaseAdvice {
fun handleHttpMessageNotReadableException( fun handleHttpMessageNotReadableException(
ex: HttpMessageNotReadableException, ex: HttpMessageNotReadableException,
request: WebRequest request: WebRequest
): WebResponse<Nothing> { ): WebResponse<Unit> {
return WebResponse.ko( return WebResponse.ko(
HttpStatus.NOT_ACCEPTABLE, HttpStatus.NOT_ACCEPTABLE,
"${HttpStatus.NOT_ACCEPTABLE.reasonPhrase}: JSON parse error" "${HttpStatus.NOT_ACCEPTABLE.reasonPhrase}: JSON parse error"
@ -107,7 +107,7 @@ class BaseAdvice {
fun handleAuthorizationDeniedException( fun handleAuthorizationDeniedException(
ex: AuthorizationDeniedException, ex: AuthorizationDeniedException,
request: WebRequest request: WebRequest
): WebResponse<Nothing> { ): WebResponse<Unit> {
return WebResponse.ko( return WebResponse.ko(
HttpStatus.FORBIDDEN, HttpStatus.FORBIDDEN,
"${HttpStatus.FORBIDDEN.reasonPhrase}: ${ex.message}" "${HttpStatus.FORBIDDEN.reasonPhrase}: ${ex.message}"
@ -120,7 +120,7 @@ class BaseAdvice {
fun handleException( fun handleException(
ex: Exception, ex: Exception,
request: WebRequest request: WebRequest
): WebResponse<Nothing> { ): WebResponse<Unit> {
return WebResponse.ko( return WebResponse.ko(
HttpStatus.INTERNAL_SERVER_ERROR, HttpStatus.INTERNAL_SERVER_ERROR,
ex.message ex.message

View File

@ -17,7 +17,8 @@ data class ClientDto(
val phone: String, val phone: String,
@field:ExpValidator.Validate @field:ExpValidator.Validate
val numExp: String, val numExp: String,
@field:ExpValidator.Validate
val numUser: String, val numUser: String,
val dateOfBirth: Date, val dateOfBirth: Date,
val deactivated: Boolean val deactivated: Boolean? = false
) )

View File

@ -15,7 +15,7 @@ class B64Validator : ConstraintValidator<B64Validator.Validate, String> {
} }
context.disableDefaultConstraintViolation() context.disableDefaultConstraintViolation()
context.buildConstraintViolationWithTemplate("Invalid value '$value' : Only 4 to 16 lowercase letters are allowed") context.buildConstraintViolationWithTemplate("Invalid value '$value' : this is not b64")
.addConstraintViolation() .addConstraintViolation()
return false return false

View File

@ -9,14 +9,14 @@ import kotlin.reflect.KClass
class ExpValidator : ConstraintValidator<ExpValidator.Validate, String> { class ExpValidator : ConstraintValidator<ExpValidator.Validate, String> {
override fun isValid(value: String?, context: ConstraintValidatorContext): Boolean { override fun isValid(value: String?, context: ConstraintValidatorContext): Boolean {
val pattern = Regex("") val pattern = Regex("\\d+/[0-9]{4}")
if (value == null || value.matches(pattern)) { if (value == null || value.matches(pattern)) {
return true return true
} }
context.disableDefaultConstraintViolation() context.disableDefaultConstraintViolation()
context.buildConstraintViolationWithTemplate("Invalid value '$value' : 8 numbers and a letter are expected.") context.buildConstraintViolationWithTemplate("Invalid value '$value' : digits/full_year is expected")
.addConstraintViolation() .addConstraintViolation()
return false return false

View File

@ -10,7 +10,7 @@ data class WebResponse<T>(
) { ) {
companion object { companion object {
fun <T> ok(): WebResponse<T> { fun ok(): WebResponse<Unit> {
return WebResponse(status = HttpStatus.OK) return WebResponse(status = HttpStatus.OK)
} }

View File

@ -1,3 +1,8 @@
#curl -X POST http://localhost:8080/api/auth/register \
# -H "Content-Type: application/json" \
# -d '{"username":"test","password":"cGFzc3dvcmQ="}'
curl -X POST http://localhost:8080/api/auth/register \ curl -X POST http://localhost:8080/api/auth/register \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d '{"username":"test","password":"cGFzc3dvcmQ="}' -d '{"username":"newtest","password":"cGFzc3dvcmQ="}'

View File

@ -0,0 +1,21 @@
TOKEN=$(curl -s -X POST http://localhost:8080/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"newtest","password":"cGFzc3dvcmQ="}' | sed -n 's/.*"token":"\([^"]*\)".*/\1/p')
curl -X GET http://localhost:8080/api/workers \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json"
# UNCOMMENT BELOW TO REGISTER USER
#
#curl -X POST http://localhost:8080/api/workers/register \
# -H "Authorization: Bearer $TOKEN" \
# -H "Content-Type: application/json" \
# -d '{"name": "TestTest","surname": "TestTest","dni": "12345678B","dateOfBirth": "1990-05-15","email": "example@domain.org","category":"FISIO"}'
#
#
#curl -X GET http://localhost:8080/api/workers \
# -H "Authorization: Bearer $TOKEN" \
# -H "Content-Type: application/json"

View File

@ -0,0 +1,8 @@
TOKEN=$(curl -s -X POST http://localhost:8080/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"test","password":"cGFzc3dvcmQ="}' | sed -n 's/.*"token":"\([^"]*\)".*/\1/p')
curl -X POST http://localhost:8080/api/clients \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "TestClient","surname": "TestClient","dni": "87654321Z","dateOfBirth": "1990-05-15","email": "client_one@mailbox.org", "deactivated":false, "phone":"665872262","numExp":"4/2024", "numUser":"100/2025"}'