diff --git a/legalconsenthub-backend/api/legalconsenthub.yml b/legalconsenthub-backend/api/legalconsenthub.yml index b40493e..273875c 100644 --- a/legalconsenthub-backend/api/legalconsenthub.yml +++ b/legalconsenthub-backend/api/legalconsenthub.yml @@ -240,48 +240,6 @@ paths: "503": $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}: parameters: - name: id @@ -607,77 +565,12 @@ components: type: object required: - id - - username - - firstname - - lastname - - email - - password - - roleId - - createdAt - - modifiedAt + - name properties: id: type: string - format: uuid - username: + name: 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: @@ -704,9 +597,9 @@ components: isTemplate: type: boolean createdBy: - type: string + $ref: "#/components/schemas/UserDto" lastModifiedBy: - type: string + $ref: "#/components/schemas/UserDto" createdAt: type: string format: date-time @@ -732,10 +625,6 @@ components: isTemplate: type: boolean default: false - createdBy: - type: string - lastModifiedBy: - type: string PagedApplicationFormDto: type: object diff --git a/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/application_form/ApplicationForm.kt b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/application_form/ApplicationForm.kt index 9fb9c37..b605875 100644 --- a/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/application_form/ApplicationForm.kt +++ b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/application_form/ApplicationForm.kt @@ -1,6 +1,9 @@ package com.betriebsratkanzlei.legalconsenthub.application_form 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.Column import jakarta.persistence.Entity @@ -8,6 +11,7 @@ import jakarta.persistence.EntityListeners import jakarta.persistence.GeneratedValue import jakarta.persistence.Id import jakarta.persistence.OneToMany +import jakarta.persistence.Embedded import org.springframework.data.annotation.CreatedDate import org.springframework.data.annotation.LastModifiedDate import org.springframework.data.jpa.domain.support.AuditingEntityListener @@ -30,11 +34,19 @@ class ApplicationForm( @Column(nullable = false) var isTemplate: Boolean, - @Column(nullable = false) - var createdBy: String = "", + @Embedded + @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) - var lastModifiedBy: String = "", + @Embedded + @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 @Column(nullable = false) diff --git a/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/application_form/ApplicationFormMapper.kt b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/application_form/ApplicationFormMapper.kt index 21857c8..d962e52 100644 --- a/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/application_form/ApplicationFormMapper.kt +++ b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/application_form/ApplicationFormMapper.kt @@ -1,20 +1,24 @@ 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.CreateApplicationFormDto +import org.springframework.security.core.context.SecurityContextHolder import org.springframework.stereotype.Component import java.time.LocalDateTime @Component -class ApplicationFormMapper(private val formElementMapper: FormElementMapper) { +class ApplicationFormMapper(private val formElementMapper: FormElementMapper, private val userMapper: UserMapper) { fun toApplicationFormDto(applicationForm: ApplicationForm): ApplicationFormDto { return ApplicationFormDto( id = applicationForm.id ?: throw IllegalStateException("ApplicationForm ID must not be null!"), name = applicationForm.name, formElements = applicationForm.formElements.map { formElementMapper.toFormElementDto(it) }, isTemplate = applicationForm.isTemplate, - createdBy = applicationForm.createdBy, - lastModifiedBy = applicationForm.lastModifiedBy, + createdBy = userMapper.toUserDto(applicationForm.createdBy), + lastModifiedBy = userMapper.toUserDto(applicationForm.lastModifiedBy), createdAt = applicationForm.createdAt ?: LocalDateTime.now(), modifiedAt = applicationForm.modifiedAt ?: LocalDateTime.now() ) @@ -26,19 +30,23 @@ class ApplicationFormMapper(private val formElementMapper: FormElementMapper) { name = applicationForm.name, formElements = applicationForm.formElements.map { formElementMapper.toFormElement(it) }.toMutableList(), isTemplate = applicationForm.isTemplate, - createdBy = applicationForm.createdBy, - lastModifiedBy = applicationForm.lastModifiedBy, + createdBy = userMapper.toUser(applicationForm.createdBy), + lastModifiedBy = userMapper.toUser(applicationForm.lastModifiedBy), createdAt = applicationForm.createdAt, modifiedAt = applicationForm.modifiedAt ) } 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( name = createApplicationFormDto.name, isTemplate = createApplicationFormDto.isTemplate, - createdBy = createApplicationFormDto.createdBy, - lastModifiedBy = createApplicationFormDto.lastModifiedBy + createdBy = createdBy, + lastModifiedBy = lastModifiedBy, ) applicationForm.formElements = createApplicationFormDto.formElements .map { formElementMapper.toFormElement(it, applicationForm) } diff --git a/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/config/CustomJwtAuthenticationConverter.kt b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/config/CustomJwtAuthenticationConverter.kt new file mode 100644 index 0000000..90d437a --- /dev/null +++ b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/config/CustomJwtAuthenticationConverter.kt @@ -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 { + override fun convert(jwt: Jwt): AbstractAuthenticationToken { + val authorities: Collection = emptyList() + + val userId = jwt.getClaimAsString("id") + val username = jwt.getClaimAsString("name") + val principal = CustomJwtTokenPrincipal(userId, username) + + return CustomJwtAuthentication(jwt, principal, authorities) + } +} diff --git a/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/config/SecurityConfig.kt b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/config/SecurityConfig.kt index 5d51376..0d3bf6a 100644 --- a/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/config/SecurityConfig.kt +++ b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/config/SecurityConfig.kt @@ -15,7 +15,10 @@ import org.springframework.security.web.SecurityFilterChain class SecurityConfig { @Bean - fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { + fun securityFilterChain( + http: HttpSecurity, + customJwtAuthenticationConverter: CustomJwtAuthenticationConverter + ): SecurityFilterChain { http { csrf { disable() } authorizeHttpRequests { @@ -24,7 +27,7 @@ class SecurityConfig { authorize(anyRequest, authenticated) } oauth2ResourceServer { - jwt { } + jwt { jwtAuthenticationConverter = customJwtAuthenticationConverter } } } diff --git a/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/security/CustomJwtAuthentication.kt b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/security/CustomJwtAuthentication.kt new file mode 100644 index 0000000..d46a5ef --- /dev/null +++ b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/security/CustomJwtAuthentication.kt @@ -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 +) : JwtAuthenticationToken( + jwt, authorities, principal.id +) { + override fun getPrincipal(): CustomJwtTokenPrincipal { + return principal + } +} diff --git a/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/security/CustomJwtTokenPrincipal.kt b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/security/CustomJwtTokenPrincipal.kt new file mode 100644 index 0000000..e8f4ddb --- /dev/null +++ b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/security/CustomJwtTokenPrincipal.kt @@ -0,0 +1,6 @@ +package com.betriebsratkanzlei.legalconsenthub.security + +data class CustomJwtTokenPrincipal( + val id: String? = null, + val name: String? = null +) diff --git a/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/user/User.kt b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/user/User.kt new file mode 100644 index 0000000..8b0324c --- /dev/null +++ b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/user/User.kt @@ -0,0 +1,9 @@ +package com.betriebsratkanzlei.legalconsenthub.user + +import jakarta.persistence.Embeddable + +@Embeddable +class User( + var name: String, + var id: String +) diff --git a/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/user/UserMapper.kt b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/user/UserMapper.kt new file mode 100644 index 0000000..f13c319 --- /dev/null +++ b/legalconsenthub-backend/src/main/kotlin/com/betriebsratkanzlei/legalconsenthub/user/UserMapper.kt @@ -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, + ) + } +} diff --git a/legalconsenthub-backend/src/main/resources/application.yaml b/legalconsenthub-backend/src/main/resources/application.yaml index e3b5fc0..ad80bae 100644 --- a/legalconsenthub-backend/src/main/resources/application.yaml +++ b/legalconsenthub-backend/src/main/resources/application.yaml @@ -22,14 +22,6 @@ spring: order_inserts: 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: enabled: true drop-first: false diff --git a/legalconsenthub-backend/src/main/resources/db/migrations/001-schema.sql b/legalconsenthub-backend/src/main/resources/db/migrations/001-schema.sql index 0b732b2..8d6d553 100644 --- a/legalconsenthub-backend/src/main/resources/db/migrations/001-schema.sql +++ b/legalconsenthub-backend/src/main/resources/db/migrations/001-schema.sql @@ -1,12 +1,14 @@ create table application_form ( - is_template boolean not null, - created_at timestamp(6) not null, - modified_at timestamp(6) not null, - id uuid not null, - created_by varchar(255) not null, - last_modified_by varchar(255) not null, - name varchar(255) not null, + is_template boolean not null, + created_at timestamp(6) not null, + modified_at timestamp(6) not null, + id uuid not null, + created_by_id 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, primary key (id) ); diff --git a/legalconsenthub/pages/application-forms/[id].vue b/legalconsenthub/pages/application-forms/[id].vue index 576e306..97f7c05 100644 --- a/legalconsenthub/pages/application-forms/[id].vue +++ b/legalconsenthub/pages/application-forms/[id].vue @@ -62,7 +62,7 @@ const applicationForm = computed({ }) const isReadOnly = computed(() => { - return applicationForm.value?.createdBy !== user.value?.name + return applicationForm.value?.createdBy.id !== user.value?.id }) async function onSubmit() { diff --git a/legalconsenthub/pages/index.vue b/legalconsenthub/pages/index.vue index b95ddba..b6870ef 100644 --- a/legalconsenthub/pages/index.vue +++ b/legalconsenthub/pages/index.vue @@ -31,11 +31,11 @@ #{{ index }} {{ applicationFormElem.name }}

- Zuletzt bearbeitet von {{ applicationFormElem.lastModifiedBy }} am + Zuletzt bearbeitet von {{ applicationFormElem.lastModifiedBy.name }} am {{ formatDate(applicationFormElem.modifiedAt) }}

- Erstellt von {{ applicationFormElem.createdBy }} am {{ formatDate(applicationFormElem.createdAt) }} + Erstellt von {{ applicationFormElem.createdBy.name }} am {{ formatDate(applicationFormElem.createdAt) }}

diff --git a/legalconsenthub/server/utils/auth.ts b/legalconsenthub/server/utils/auth.ts index 4035891..d75e501 100644 --- a/legalconsenthub/server/utils/auth.ts +++ b/legalconsenthub/server/utils/auth.ts @@ -9,7 +9,8 @@ export const auth = betterAuth({ plugins: [ jwt({ jwt: { - issuer: 'http://192.168.178.105:3001' + issuer: 'http://192.168.178.105:3001', + expirationTime: '48h' }, jwks: { keyPairConfig: {