feat(fullstack): Read user out of JWT and persist with created and last modified
This commit is contained in:
@@ -240,48 +240,6 @@ paths:
|
|||||||
"503":
|
"503":
|
||||||
$ref: "https://api.swaggerhub.com/domains/smartbear-public/ProblemDetails/1.0.0#/components/responses/ServiceUnavailable"
|
$ref: "https://api.swaggerhub.com/domains/smartbear-public/ProblemDetails/1.0.0#/components/responses/ServiceUnavailable"
|
||||||
|
|
||||||
/users:
|
|
||||||
get:
|
|
||||||
summary: Get all users
|
|
||||||
operationId: getAllUsers
|
|
||||||
tags:
|
|
||||||
- user
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: Paged list of users
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: "#/components/schemas/PagedUserDto"
|
|
||||||
"500":
|
|
||||||
description: Internal server error
|
|
||||||
post:
|
|
||||||
summary: Create a new user
|
|
||||||
operationId: createUser
|
|
||||||
tags:
|
|
||||||
- user
|
|
||||||
requestBody:
|
|
||||||
required: true
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: "#/components/schemas/CreateUserDTO"
|
|
||||||
responses:
|
|
||||||
"201":
|
|
||||||
description: Successfully created user
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: "#/components/schemas/UserDto"
|
|
||||||
"400":
|
|
||||||
$ref: "https://api.swaggerhub.com/domains/smartbear-public/ProblemDetails/1.0.0#/components/responses/BadRequest"
|
|
||||||
"401":
|
|
||||||
$ref: "https://api.swaggerhub.com/domains/smartbear-public/ProblemDetails/1.0.0#/components/responses/Unauthorized"
|
|
||||||
"500":
|
|
||||||
$ref: "https://api.swaggerhub.com/domains/smartbear-public/ProblemDetails/1.0.0#/components/responses/ServerError"
|
|
||||||
"503":
|
|
||||||
$ref: "https://api.swaggerhub.com/domains/smartbear-public/ProblemDetails/1.0.0#/components/responses/ServiceUnavailable"
|
|
||||||
|
|
||||||
/users/{id}:
|
/users/{id}:
|
||||||
parameters:
|
parameters:
|
||||||
- name: id
|
- name: id
|
||||||
@@ -607,77 +565,12 @@ components:
|
|||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
- id
|
- id
|
||||||
- username
|
- name
|
||||||
- firstname
|
|
||||||
- lastname
|
|
||||||
- email
|
|
||||||
- password
|
|
||||||
- roleId
|
|
||||||
- createdAt
|
|
||||||
- modifiedAt
|
|
||||||
properties:
|
properties:
|
||||||
id:
|
id:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
name:
|
||||||
username:
|
|
||||||
type: string
|
type: string
|
||||||
firstName:
|
|
||||||
type: string
|
|
||||||
lastName:
|
|
||||||
type: string
|
|
||||||
email:
|
|
||||||
type: string
|
|
||||||
format: email
|
|
||||||
password:
|
|
||||||
type: string
|
|
||||||
roleId:
|
|
||||||
type: string
|
|
||||||
format: uuid
|
|
||||||
createdAt:
|
|
||||||
type: string
|
|
||||||
format: date-time
|
|
||||||
modifiedAt:
|
|
||||||
type: string
|
|
||||||
format: date-time
|
|
||||||
|
|
||||||
CreateUserDTO:
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- username
|
|
||||||
- firstname
|
|
||||||
- lastname
|
|
||||||
- email
|
|
||||||
- password
|
|
||||||
- roleId
|
|
||||||
- createdAt
|
|
||||||
- modifiedAt
|
|
||||||
properties:
|
|
||||||
username:
|
|
||||||
type: string
|
|
||||||
firstName:
|
|
||||||
type: string
|
|
||||||
lastName:
|
|
||||||
type: string
|
|
||||||
email:
|
|
||||||
type: string
|
|
||||||
format: email
|
|
||||||
password:
|
|
||||||
type: string
|
|
||||||
roleId:
|
|
||||||
type: string
|
|
||||||
format: uuid
|
|
||||||
|
|
||||||
PagedUserDto:
|
|
||||||
type: object
|
|
||||||
allOf:
|
|
||||||
- $ref: "#/components/schemas/Page"
|
|
||||||
required:
|
|
||||||
- content
|
|
||||||
properties:
|
|
||||||
content:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: "#/components/schemas/UserDto"
|
|
||||||
|
|
||||||
####### ApplicationFormDto #######
|
####### ApplicationFormDto #######
|
||||||
ApplicationFormDto:
|
ApplicationFormDto:
|
||||||
@@ -704,9 +597,9 @@ components:
|
|||||||
isTemplate:
|
isTemplate:
|
||||||
type: boolean
|
type: boolean
|
||||||
createdBy:
|
createdBy:
|
||||||
type: string
|
$ref: "#/components/schemas/UserDto"
|
||||||
lastModifiedBy:
|
lastModifiedBy:
|
||||||
type: string
|
$ref: "#/components/schemas/UserDto"
|
||||||
createdAt:
|
createdAt:
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
@@ -732,10 +625,6 @@ components:
|
|||||||
isTemplate:
|
isTemplate:
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: false
|
||||||
createdBy:
|
|
||||||
type: string
|
|
||||||
lastModifiedBy:
|
|
||||||
type: string
|
|
||||||
|
|
||||||
PagedApplicationFormDto:
|
PagedApplicationFormDto:
|
||||||
type: object
|
type: object
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package com.betriebsratkanzlei.legalconsenthub.application_form
|
package com.betriebsratkanzlei.legalconsenthub.application_form
|
||||||
|
|
||||||
import com.betriebsratkanzlei.legalconsenthub.form_element.FormElement
|
import com.betriebsratkanzlei.legalconsenthub.form_element.FormElement
|
||||||
|
import com.betriebsratkanzlei.legalconsenthub.user.User
|
||||||
|
import jakarta.persistence.AttributeOverride
|
||||||
|
import jakarta.persistence.AttributeOverrides
|
||||||
import jakarta.persistence.CascadeType
|
import jakarta.persistence.CascadeType
|
||||||
import jakarta.persistence.Column
|
import jakarta.persistence.Column
|
||||||
import jakarta.persistence.Entity
|
import jakarta.persistence.Entity
|
||||||
@@ -8,6 +11,7 @@ import jakarta.persistence.EntityListeners
|
|||||||
import jakarta.persistence.GeneratedValue
|
import jakarta.persistence.GeneratedValue
|
||||||
import jakarta.persistence.Id
|
import jakarta.persistence.Id
|
||||||
import jakarta.persistence.OneToMany
|
import jakarta.persistence.OneToMany
|
||||||
|
import jakarta.persistence.Embedded
|
||||||
import org.springframework.data.annotation.CreatedDate
|
import org.springframework.data.annotation.CreatedDate
|
||||||
import org.springframework.data.annotation.LastModifiedDate
|
import org.springframework.data.annotation.LastModifiedDate
|
||||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener
|
import org.springframework.data.jpa.domain.support.AuditingEntityListener
|
||||||
@@ -30,11 +34,19 @@ class ApplicationForm(
|
|||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
var isTemplate: Boolean,
|
var isTemplate: Boolean,
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Embedded
|
||||||
var createdBy: String = "",
|
@AttributeOverrides(
|
||||||
|
AttributeOverride(name = "id", column = Column(name = "created_by_id", nullable = false)),
|
||||||
|
AttributeOverride(name = "name", column = Column(name = "created_by_name", nullable = false))
|
||||||
|
)
|
||||||
|
var createdBy: User,
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Embedded
|
||||||
var lastModifiedBy: String = "",
|
@AttributeOverrides(
|
||||||
|
AttributeOverride(name = "id", column = Column(name = "last_modified_by_id", nullable = false)),
|
||||||
|
AttributeOverride(name = "name", column = Column(name = "last_modified_by_name", nullable = false))
|
||||||
|
)
|
||||||
|
var lastModifiedBy: User,
|
||||||
|
|
||||||
@CreatedDate
|
@CreatedDate
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
|
|||||||
@@ -1,20 +1,24 @@
|
|||||||
package com.betriebsratkanzlei.legalconsenthub.application_form
|
package com.betriebsratkanzlei.legalconsenthub.application_form
|
||||||
|
|
||||||
|
import com.betriebsratkanzlei.legalconsenthub.security.CustomJwtTokenPrincipal
|
||||||
|
import com.betriebsratkanzlei.legalconsenthub.user.User
|
||||||
|
import com.betriebsratkanzlei.legalconsenthub.user.UserMapper
|
||||||
import com.betriebsratkanzlei.legalconsenthub_api.model.ApplicationFormDto
|
import com.betriebsratkanzlei.legalconsenthub_api.model.ApplicationFormDto
|
||||||
import com.betriebsratkanzlei.legalconsenthub_api.model.CreateApplicationFormDto
|
import com.betriebsratkanzlei.legalconsenthub_api.model.CreateApplicationFormDto
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
class ApplicationFormMapper(private val formElementMapper: FormElementMapper) {
|
class ApplicationFormMapper(private val formElementMapper: FormElementMapper, private val userMapper: UserMapper) {
|
||||||
fun toApplicationFormDto(applicationForm: ApplicationForm): ApplicationFormDto {
|
fun toApplicationFormDto(applicationForm: ApplicationForm): ApplicationFormDto {
|
||||||
return ApplicationFormDto(
|
return ApplicationFormDto(
|
||||||
id = applicationForm.id ?: throw IllegalStateException("ApplicationForm ID must not be null!"),
|
id = applicationForm.id ?: throw IllegalStateException("ApplicationForm ID must not be null!"),
|
||||||
name = applicationForm.name,
|
name = applicationForm.name,
|
||||||
formElements = applicationForm.formElements.map { formElementMapper.toFormElementDto(it) },
|
formElements = applicationForm.formElements.map { formElementMapper.toFormElementDto(it) },
|
||||||
isTemplate = applicationForm.isTemplate,
|
isTemplate = applicationForm.isTemplate,
|
||||||
createdBy = applicationForm.createdBy,
|
createdBy = userMapper.toUserDto(applicationForm.createdBy),
|
||||||
lastModifiedBy = applicationForm.lastModifiedBy,
|
lastModifiedBy = userMapper.toUserDto(applicationForm.lastModifiedBy),
|
||||||
createdAt = applicationForm.createdAt ?: LocalDateTime.now(),
|
createdAt = applicationForm.createdAt ?: LocalDateTime.now(),
|
||||||
modifiedAt = applicationForm.modifiedAt ?: LocalDateTime.now()
|
modifiedAt = applicationForm.modifiedAt ?: LocalDateTime.now()
|
||||||
)
|
)
|
||||||
@@ -26,19 +30,23 @@ class ApplicationFormMapper(private val formElementMapper: FormElementMapper) {
|
|||||||
name = applicationForm.name,
|
name = applicationForm.name,
|
||||||
formElements = applicationForm.formElements.map { formElementMapper.toFormElement(it) }.toMutableList(),
|
formElements = applicationForm.formElements.map { formElementMapper.toFormElement(it) }.toMutableList(),
|
||||||
isTemplate = applicationForm.isTemplate,
|
isTemplate = applicationForm.isTemplate,
|
||||||
createdBy = applicationForm.createdBy,
|
createdBy = userMapper.toUser(applicationForm.createdBy),
|
||||||
lastModifiedBy = applicationForm.lastModifiedBy,
|
lastModifiedBy = userMapper.toUser(applicationForm.lastModifiedBy),
|
||||||
createdAt = applicationForm.createdAt,
|
createdAt = applicationForm.createdAt,
|
||||||
modifiedAt = applicationForm.modifiedAt
|
modifiedAt = applicationForm.modifiedAt
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toApplicationForm(createApplicationFormDto: CreateApplicationFormDto): ApplicationForm {
|
fun toApplicationForm(createApplicationFormDto: CreateApplicationFormDto): ApplicationForm {
|
||||||
|
val principal = SecurityContextHolder.getContext().authentication.principal as CustomJwtTokenPrincipal
|
||||||
|
val createdBy = User(principal.name ?: "UNKNOWN USER", principal.id ?: "")
|
||||||
|
val lastModifiedBy = User(principal.name ?: "UNKNOWN USER", principal.id ?: "")
|
||||||
|
|
||||||
val applicationForm = ApplicationForm(
|
val applicationForm = ApplicationForm(
|
||||||
name = createApplicationFormDto.name,
|
name = createApplicationFormDto.name,
|
||||||
isTemplate = createApplicationFormDto.isTemplate,
|
isTemplate = createApplicationFormDto.isTemplate,
|
||||||
createdBy = createApplicationFormDto.createdBy,
|
createdBy = createdBy,
|
||||||
lastModifiedBy = createApplicationFormDto.lastModifiedBy
|
lastModifiedBy = lastModifiedBy,
|
||||||
)
|
)
|
||||||
applicationForm.formElements = createApplicationFormDto.formElements
|
applicationForm.formElements = createApplicationFormDto.formElements
|
||||||
.map { formElementMapper.toFormElement(it, applicationForm) }
|
.map { formElementMapper.toFormElement(it, applicationForm) }
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.betriebsratkanzlei.legalconsenthub.config
|
||||||
|
|
||||||
|
import com.betriebsratkanzlei.legalconsenthub.security.CustomJwtAuthentication
|
||||||
|
import com.betriebsratkanzlei.legalconsenthub.security.CustomJwtTokenPrincipal
|
||||||
|
import org.springframework.core.convert.converter.Converter
|
||||||
|
import org.springframework.security.authentication.AbstractAuthenticationToken
|
||||||
|
import org.springframework.security.core.GrantedAuthority
|
||||||
|
import org.springframework.security.oauth2.jwt.Jwt
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class CustomJwtAuthenticationConverter : Converter<Jwt, AbstractAuthenticationToken> {
|
||||||
|
override fun convert(jwt: Jwt): AbstractAuthenticationToken {
|
||||||
|
val authorities: Collection<GrantedAuthority> = emptyList()
|
||||||
|
|
||||||
|
val userId = jwt.getClaimAsString("id")
|
||||||
|
val username = jwt.getClaimAsString("name")
|
||||||
|
val principal = CustomJwtTokenPrincipal(userId, username)
|
||||||
|
|
||||||
|
return CustomJwtAuthentication(jwt, principal, authorities)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,10 @@ import org.springframework.security.web.SecurityFilterChain
|
|||||||
class SecurityConfig {
|
class SecurityConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
|
fun securityFilterChain(
|
||||||
|
http: HttpSecurity,
|
||||||
|
customJwtAuthenticationConverter: CustomJwtAuthenticationConverter
|
||||||
|
): SecurityFilterChain {
|
||||||
http {
|
http {
|
||||||
csrf { disable() }
|
csrf { disable() }
|
||||||
authorizeHttpRequests {
|
authorizeHttpRequests {
|
||||||
@@ -24,7 +27,7 @@ class SecurityConfig {
|
|||||||
authorize(anyRequest, authenticated)
|
authorize(anyRequest, authenticated)
|
||||||
}
|
}
|
||||||
oauth2ResourceServer {
|
oauth2ResourceServer {
|
||||||
jwt { }
|
jwt { jwtAuthenticationConverter = customJwtAuthenticationConverter }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.betriebsratkanzlei.legalconsenthub.security
|
||||||
|
|
||||||
|
import org.springframework.security.core.GrantedAuthority
|
||||||
|
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken
|
||||||
|
import org.springframework.security.oauth2.jwt.Jwt
|
||||||
|
|
||||||
|
class CustomJwtAuthentication(
|
||||||
|
jwt: Jwt,
|
||||||
|
private val principal: CustomJwtTokenPrincipal,
|
||||||
|
authorities: Collection<GrantedAuthority>
|
||||||
|
) : JwtAuthenticationToken(
|
||||||
|
jwt, authorities, principal.id
|
||||||
|
) {
|
||||||
|
override fun getPrincipal(): CustomJwtTokenPrincipal {
|
||||||
|
return principal
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package com.betriebsratkanzlei.legalconsenthub.security
|
||||||
|
|
||||||
|
data class CustomJwtTokenPrincipal(
|
||||||
|
val id: String? = null,
|
||||||
|
val name: String? = null
|
||||||
|
)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.betriebsratkanzlei.legalconsenthub.user
|
||||||
|
|
||||||
|
import jakarta.persistence.Embeddable
|
||||||
|
|
||||||
|
@Embeddable
|
||||||
|
class User(
|
||||||
|
var name: String,
|
||||||
|
var id: String
|
||||||
|
)
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.betriebsratkanzlei.legalconsenthub.user
|
||||||
|
|
||||||
|
import com.betriebsratkanzlei.legalconsenthub_api.model.UserDto
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class UserMapper() {
|
||||||
|
fun toUserDto(user: User): UserDto {
|
||||||
|
return UserDto(
|
||||||
|
id = user.id,
|
||||||
|
name = user.name,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toUser(userDto: UserDto): User {
|
||||||
|
return User(
|
||||||
|
id = userDto.id,
|
||||||
|
name = userDto.name,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,14 +22,6 @@ spring:
|
|||||||
order_inserts: true
|
order_inserts: true
|
||||||
enable_lazy_load_no_trans: true
|
enable_lazy_load_no_trans: true
|
||||||
|
|
||||||
# security:
|
|
||||||
# oauth2:
|
|
||||||
# resourceserver:
|
|
||||||
# jwt:
|
|
||||||
# issuer-uri: http://192.168.178.105:3001
|
|
||||||
# jwk-set-uri: http://192.168.178.105:3001/api/auth/jwks
|
|
||||||
# jws-algorithms: ES512
|
|
||||||
|
|
||||||
liquibase:
|
liquibase:
|
||||||
enabled: true
|
enabled: true
|
||||||
drop-first: false
|
drop-first: false
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ create table application_form
|
|||||||
created_at timestamp(6) not null,
|
created_at timestamp(6) not null,
|
||||||
modified_at timestamp(6) not null,
|
modified_at timestamp(6) not null,
|
||||||
id uuid not null,
|
id uuid not null,
|
||||||
created_by varchar(255) not null,
|
created_by_id varchar(255) not null,
|
||||||
last_modified_by varchar(255) not null,
|
created_by_name varchar(255) not null,
|
||||||
|
last_modified_by_id varchar(255) not null,
|
||||||
|
last_modified_by_name varchar(255) not null,
|
||||||
name varchar(255) not null,
|
name varchar(255) not null,
|
||||||
primary key (id)
|
primary key (id)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ const applicationForm = computed({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const isReadOnly = computed(() => {
|
const isReadOnly = computed(() => {
|
||||||
return applicationForm.value?.createdBy !== user.value?.name
|
return applicationForm.value?.createdBy.id !== user.value?.id
|
||||||
})
|
})
|
||||||
|
|
||||||
async function onSubmit() {
|
async function onSubmit() {
|
||||||
|
|||||||
@@ -31,11 +31,11 @@
|
|||||||
#{{ index }} {{ applicationFormElem.name }}
|
#{{ index }} {{ applicationFormElem.name }}
|
||||||
</p>
|
</p>
|
||||||
<p class="text-(--ui-text-muted) text-sm">
|
<p class="text-(--ui-text-muted) text-sm">
|
||||||
Zuletzt bearbeitet von {{ applicationFormElem.lastModifiedBy }} am
|
Zuletzt bearbeitet von {{ applicationFormElem.lastModifiedBy.name }} am
|
||||||
{{ formatDate(applicationFormElem.modifiedAt) }}
|
{{ formatDate(applicationFormElem.modifiedAt) }}
|
||||||
</p>
|
</p>
|
||||||
<p class="text-(--ui-text-muted) text-sm">
|
<p class="text-(--ui-text-muted) text-sm">
|
||||||
Erstellt von {{ applicationFormElem.createdBy }} am {{ formatDate(applicationFormElem.createdAt) }}
|
Erstellt von {{ applicationFormElem.createdBy.name }} am {{ formatDate(applicationFormElem.createdAt) }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ export const auth = betterAuth({
|
|||||||
plugins: [
|
plugins: [
|
||||||
jwt({
|
jwt({
|
||||||
jwt: {
|
jwt: {
|
||||||
issuer: 'http://192.168.178.105:3001'
|
issuer: 'http://192.168.178.105:3001',
|
||||||
|
expirationTime: '48h'
|
||||||
},
|
},
|
||||||
jwks: {
|
jwks: {
|
||||||
keyPairConfig: {
|
keyPairConfig: {
|
||||||
|
|||||||
Reference in New Issue
Block a user