feat(#1): Add permission and role model

This commit is contained in:
2025-10-31 09:26:37 +01:00
parent 36364a7977
commit 1997877168
12 changed files with 218 additions and 31 deletions

View File

@@ -9,6 +9,7 @@ import org.springframework.core.io.Resource
import org.springframework.http.HttpHeaders import org.springframework.http.HttpHeaders
import org.springframework.http.MediaType import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity import org.springframework.http.ResponseEntity
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.web.bind.annotation.RestController import org.springframework.web.bind.annotation.RestController
import java.util.UUID import java.util.UUID
@@ -20,6 +21,7 @@ class ApplicationFormController(
val applicationFormFormatService: ApplicationFormFormatService val applicationFormFormatService: ApplicationFormFormatService
) : ApplicationFormApi { ) : ApplicationFormApi {
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')")
override fun createApplicationForm(createApplicationFormDto: CreateApplicationFormDto): ResponseEntity<ApplicationFormDto> { override fun createApplicationForm(createApplicationFormDto: CreateApplicationFormDto): ResponseEntity<ApplicationFormDto> {
val updatedCreateApplicationFormDto = createApplicationFormDto.copy(isTemplate = false) val updatedCreateApplicationFormDto = createApplicationFormDto.copy(isTemplate = false)
return ResponseEntity.ok( return ResponseEntity.ok(
@@ -29,6 +31,7 @@ class ApplicationFormController(
) )
} }
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL', 'EMPLOYEE')")
override fun getAllApplicationForms(organizationId: String?): ResponseEntity<PagedApplicationFormDto> { override fun getAllApplicationForms(organizationId: String?): ResponseEntity<PagedApplicationFormDto> {
return ResponseEntity.ok( return ResponseEntity.ok(
pagedApplicationFormMapper.toPagedApplicationFormDto( pagedApplicationFormMapper.toPagedApplicationFormDto(
@@ -37,6 +40,7 @@ class ApplicationFormController(
) )
} }
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL', 'EMPLOYEE')")
override fun getApplicationFormById(id: UUID): ResponseEntity<ApplicationFormDto> { override fun getApplicationFormById(id: UUID): ResponseEntity<ApplicationFormDto> {
return ResponseEntity.ok( return ResponseEntity.ok(
applicationFormMapper.toApplicationFormDto( applicationFormMapper.toApplicationFormDto(
@@ -45,6 +49,7 @@ class ApplicationFormController(
) )
} }
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL', 'EMPLOYEE')")
override fun getApplicationFormHtml(id: UUID): ResponseEntity<String> { override fun getApplicationFormHtml(id: UUID): ResponseEntity<String> {
val applicationForm = applicationFormService.getApplicationFormById(id) val applicationForm = applicationFormService.getApplicationFormById(id)
return ResponseEntity.ok( return ResponseEntity.ok(
@@ -52,6 +57,7 @@ class ApplicationFormController(
) )
} }
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL', 'EMPLOYEE')")
override fun getApplicationFormPdf(id: UUID): ResponseEntity<Resource> { override fun getApplicationFormPdf(id: UUID): ResponseEntity<Resource> {
val applicationForm = applicationFormService.getApplicationFormById(id) val applicationForm = applicationFormService.getApplicationFormById(id)
val pdfBytes = applicationFormFormatService.generatePdf(applicationForm) val pdfBytes = applicationFormFormatService.generatePdf(applicationForm)
@@ -62,6 +68,7 @@ class ApplicationFormController(
.body(resource) .body(resource)
} }
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')")
override fun updateApplicationForm( override fun updateApplicationForm(
id: UUID, id: UUID,
applicationFormDto: ApplicationFormDto applicationFormDto: ApplicationFormDto
@@ -73,11 +80,13 @@ class ApplicationFormController(
) )
} }
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')")
override fun deleteApplicationForm(id: UUID): ResponseEntity<Unit> { override fun deleteApplicationForm(id: UUID): ResponseEntity<Unit> {
applicationFormService.deleteApplicationFormByID(id) applicationFormService.deleteApplicationFormByID(id)
return ResponseEntity.noContent().build() return ResponseEntity.noContent().build()
} }
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')")
override fun submitApplicationForm(id: UUID): ResponseEntity<ApplicationFormDto> { override fun submitApplicationForm(id: UUID): ResponseEntity<ApplicationFormDto> {
return ResponseEntity.ok( return ResponseEntity.ok(
applicationFormMapper.toApplicationFormDto( applicationFormMapper.toApplicationFormDto(

View File

@@ -7,6 +7,7 @@ import com.betriebsratkanzlei.legalconsenthub_api.model.ApplicationFormDto
import com.betriebsratkanzlei.legalconsenthub_api.model.CreateApplicationFormDto import com.betriebsratkanzlei.legalconsenthub_api.model.CreateApplicationFormDto
import com.betriebsratkanzlei.legalconsenthub_api.model.PagedApplicationFormDto import com.betriebsratkanzlei.legalconsenthub_api.model.PagedApplicationFormDto
import org.springframework.http.ResponseEntity import org.springframework.http.ResponseEntity
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.web.bind.annotation.RestController import org.springframework.web.bind.annotation.RestController
import java.util.UUID import java.util.UUID
@@ -17,6 +18,7 @@ class ApplicationFormTemplateController(
val applicationFormMapper: ApplicationFormMapper, val applicationFormMapper: ApplicationFormMapper,
) : ApplicationFormTemplateApi { ) : ApplicationFormTemplateApi {
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')")
override fun createApplicationFormTemplate(createApplicationFormDto: CreateApplicationFormDto): ResponseEntity<ApplicationFormDto> { override fun createApplicationFormTemplate(createApplicationFormDto: CreateApplicationFormDto): ResponseEntity<ApplicationFormDto> {
return ResponseEntity.ok( return ResponseEntity.ok(
applicationFormMapper.toApplicationFormDto( applicationFormMapper.toApplicationFormDto(
@@ -25,6 +27,7 @@ class ApplicationFormTemplateController(
) )
} }
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')")
override fun getAllApplicationFormTemplates(): ResponseEntity<PagedApplicationFormDto> { override fun getAllApplicationFormTemplates(): ResponseEntity<PagedApplicationFormDto> {
return ResponseEntity.ok( return ResponseEntity.ok(
pagedApplicationFormMapper.toPagedApplicationFormDto( pagedApplicationFormMapper.toPagedApplicationFormDto(
@@ -33,6 +36,7 @@ class ApplicationFormTemplateController(
) )
} }
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')")
override fun getApplicationFormTemplateById(id: UUID): ResponseEntity<ApplicationFormDto> { override fun getApplicationFormTemplateById(id: UUID): ResponseEntity<ApplicationFormDto> {
return ResponseEntity.ok( return ResponseEntity.ok(
applicationFormMapper.toApplicationFormDto( applicationFormMapper.toApplicationFormDto(
@@ -41,6 +45,7 @@ class ApplicationFormTemplateController(
) )
} }
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')")
override fun updateApplicationFormTemplate( override fun updateApplicationFormTemplate(
id: UUID, id: UUID,
applicationFormDto: ApplicationFormDto applicationFormDto: ApplicationFormDto
@@ -52,6 +57,7 @@ class ApplicationFormTemplateController(
) )
} }
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')")
override fun deleteApplicationFormTemplate(id: UUID): ResponseEntity<Unit> { override fun deleteApplicationFormTemplate(id: UUID): ResponseEntity<Unit> {
applicationFormTemplateService.deleteApplicationFormTemplateByID(id) applicationFormTemplateService.deleteApplicationFormTemplateByID(id)
return ResponseEntity.noContent().build() return ResponseEntity.noContent().build()

View File

@@ -5,6 +5,7 @@ import com.betriebsratkanzlei.legalconsenthub_api.model.CommentDto
import com.betriebsratkanzlei.legalconsenthub_api.model.CreateCommentDto import com.betriebsratkanzlei.legalconsenthub_api.model.CreateCommentDto
import com.betriebsratkanzlei.legalconsenthub_api.model.PagedCommentDto import com.betriebsratkanzlei.legalconsenthub_api.model.PagedCommentDto
import org.springframework.http.ResponseEntity import org.springframework.http.ResponseEntity
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.web.bind.annotation.RestController import org.springframework.web.bind.annotation.RestController
import java.util.UUID import java.util.UUID
@@ -12,6 +13,7 @@ import java.util.UUID
class CommentController( class CommentController(
val commentService: CommentService, val commentMapper: CommentMapper, val pagedCommentMapper: PagedCommentMapper val commentService: CommentService, val commentMapper: CommentMapper, val pagedCommentMapper: PagedCommentMapper
) : CommentApi { ) : CommentApi {
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL', 'EMPLOYEE')")
override fun createComment( override fun createComment(
applicationFormId: UUID, applicationFormId: UUID,
formElementId: UUID, formElementId: UUID,
@@ -24,6 +26,7 @@ class CommentController(
) )
} }
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL', 'EMPLOYEE')")
override fun getCommentsByApplicationFormId(applicationFormId: UUID): ResponseEntity<PagedCommentDto> { override fun getCommentsByApplicationFormId(applicationFormId: UUID): ResponseEntity<PagedCommentDto> {
return ResponseEntity.ok( return ResponseEntity.ok(
pagedCommentMapper.toPagedCommentDto( pagedCommentMapper.toPagedCommentDto(
@@ -32,6 +35,7 @@ class CommentController(
) )
} }
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL', 'EMPLOYEE')")
override fun updateComment(id: UUID, commentDto: CommentDto): ResponseEntity<CommentDto> { override fun updateComment(id: UUID, commentDto: CommentDto): ResponseEntity<CommentDto> {
return ResponseEntity.ok( return ResponseEntity.ok(
commentMapper.toCommentDto( commentMapper.toCommentDto(
@@ -40,6 +44,7 @@ class CommentController(
) )
} }
@PreAuthorize("hasAnyRole('CHIEF_EXECUTIVE_OFFICER', 'BUSINESS_DEPARTMENT', 'IT_DEPARTMENT', 'HUMAN_RESOURCES', 'HEAD_OF_WORKS_COUNCIL', 'WORKS_COUNCIL')")
override fun deleteComment(id: UUID): ResponseEntity<Unit> { override fun deleteComment(id: UUID): ResponseEntity<Unit> {
commentService.deleteCommentByID(id) commentService.deleteCommentByID(id)
return ResponseEntity.noContent().build() return ResponseEntity.noContent().build()

View File

@@ -3,18 +3,15 @@ package com.betriebsratkanzlei.legalconsenthub.config
import com.betriebsratkanzlei.legalconsenthub.security.CustomJwtAuthenticationConverter import com.betriebsratkanzlei.legalconsenthub.security.CustomJwtAuthenticationConverter
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.core.annotation.Order 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
import org.springframework.security.config.annotation.web.invoke import org.springframework.security.config.annotation.web.invoke
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm
import org.springframework.security.oauth2.jwt.JwtDecoder
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder
import org.springframework.security.web.SecurityFilterChain import org.springframework.security.web.SecurityFilterChain
import org.springframework.http.HttpMethod
@Configuration @Configuration
@EnableWebSecurity @EnableWebSecurity
@EnableMethodSecurity
class SecurityConfig { class SecurityConfig {
@Bean @Bean

View File

@@ -3,18 +3,23 @@ package com.betriebsratkanzlei.legalconsenthub.security
import org.springframework.core.convert.converter.Converter import org.springframework.core.convert.converter.Converter
import org.springframework.security.authentication.AbstractAuthenticationToken import org.springframework.security.authentication.AbstractAuthenticationToken
import org.springframework.security.core.GrantedAuthority import org.springframework.security.core.GrantedAuthority
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.oauth2.jwt.Jwt import org.springframework.security.oauth2.jwt.Jwt
import org.springframework.stereotype.Component import org.springframework.stereotype.Component
@Component @Component
class CustomJwtAuthenticationConverter : Converter<Jwt, AbstractAuthenticationToken> { class CustomJwtAuthenticationConverter : Converter<Jwt, AbstractAuthenticationToken> {
override fun convert(jwt: Jwt): AbstractAuthenticationToken { override fun convert(jwt: Jwt): AbstractAuthenticationToken {
val authorities: Collection<GrantedAuthority> = emptyList()
val userId = jwt.subject val userId = jwt.subject
val username = jwt.getClaimAsString("name") val username = jwt.getClaimAsString("name")
val realmAccess = jwt.getClaimAsMap("realm_access")
val roles = (realmAccess?.get("roles") as? List<*>)?.mapNotNull { it as? String } ?: emptyList() val resourceAccess = jwt.getClaimAsMap("resource_access") as? Map<*, *>
val legalconsenthubResource = resourceAccess?.get("legalconsenthub") as? Map<*, *>
val roles = (legalconsenthubResource?.get("roles") as? List<*>)?.mapNotNull { it as? String } ?: emptyList()
val authorities: Collection<GrantedAuthority> = roles.map { role ->
SimpleGrantedAuthority("ROLE_$role")
}
val principal = CustomJwtTokenPrincipal(userId, username, roles) val principal = CustomJwtTokenPrincipal(userId, username, roles)

View File

@@ -0,0 +1,152 @@
export type Permission =
| 'application-form:read'
| 'application-form:write'
| 'application-form:sign'
| 'application-form-template:add'
| 'application-form-template:edit'
| 'application-form-template:delete'
| 'comment:add'
| 'comment:edit'
| 'comment:delete'
export type Role =
| 'CHIEF_EXECUTIVE_OFFICER'
| 'BUSINESS_DEPARTMENT'
| 'IT_DEPARTMENT'
| 'HUMAN_RESOURCES'
| 'HEAD_OF_WORKS_COUNCIL'
| 'WORKS_COUNCIL'
| 'EMPLOYEE'
const ROLE_PERMISSIONS: Record<Role, Permission[]> = {
CHIEF_EXECUTIVE_OFFICER: [
'application-form:read',
'application-form:write',
'application-form:sign',
'application-form-template:add',
'application-form-template:edit',
'application-form-template:delete',
'comment:add',
'comment:edit',
'comment:delete'
],
HEAD_OF_WORKS_COUNCIL: [
'application-form:read',
'application-form:write',
'application-form:sign',
'application-form-template:add',
'application-form-template:edit',
'application-form-template:delete',
'comment:add',
'comment:edit',
'comment:delete'
],
BUSINESS_DEPARTMENT: [
'application-form:read',
'application-form:write',
'application-form-template:add',
'application-form-template:edit',
'application-form-template:delete',
'comment:add',
'comment:edit',
'comment:delete'
],
IT_DEPARTMENT: [
'application-form:read',
'application-form:write',
'application-form-template:add',
'application-form-template:edit',
'application-form-template:delete',
'comment:add',
'comment:edit',
'comment:delete'
],
HUMAN_RESOURCES: [
'application-form:read',
'application-form:write',
'application-form-template:add',
'application-form-template:edit',
'application-form-template:delete',
'comment:add',
'comment:edit',
'comment:delete'
],
WORKS_COUNCIL: [
'application-form:read',
'application-form:write',
'application-form-template:add',
'application-form-template:edit',
'application-form-template:delete',
'comment:add',
'comment:edit',
'comment:delete'
],
EMPLOYEE: ['application-form:read', 'comment:add', 'comment:edit']
}
export const usePermissions = () => {
const { user } = useUserSession()
const userRoles = computed<Role[]>(() => {
return (user.value?.roles ?? []) as Role[]
})
const userPermissions = computed<Permission[]>(() => {
const permissions = new Set<Permission>()
userRoles.value.forEach((role) => {
const rolePermissions = ROLE_PERMISSIONS[role] ?? []
rolePermissions.forEach((permission) => permissions.add(permission))
})
return Array.from(permissions)
})
const hasPermission = (permission: Permission): boolean => {
return userPermissions.value.includes(permission)
}
const hasAnyPermission = (permissions: Permission[]): boolean => {
return permissions.some((permission) => hasPermission(permission))
}
const hasAllPermissions = (permissions: Permission[]): boolean => {
return permissions.every((permission) => hasPermission(permission))
}
const hasRole = (role: Role): boolean => {
return userRoles.value.includes(role)
}
const hasAnyRole = (roles: Role[]): boolean => {
return roles.some((role) => hasRole(role))
}
const canReadApplicationForms = computed(() => hasPermission('application-form:read'))
const canWriteApplicationForms = computed(() => hasPermission('application-form:write'))
const canSignApplicationForms = computed(() => hasPermission('application-form:sign'))
const canAddTemplate = computed(() => hasPermission('application-form-template:add'))
const canEditTemplate = computed(() => hasPermission('application-form-template:edit'))
const canDeleteTemplate = computed(() => hasPermission('application-form-template:delete'))
const canAddComment = computed(() => hasPermission('comment:add'))
const canEditComment = computed(() => hasPermission('comment:edit'))
const canDeleteComment = computed(() => hasPermission('comment:delete'))
return {
userRoles,
userPermissions,
hasPermission,
hasAnyPermission,
hasAllPermissions,
hasRole,
hasAnyRole,
canReadApplicationForms,
canWriteApplicationForms,
canSignApplicationForms,
canAddTemplate,
canEditTemplate,
canDeleteTemplate,
canAddComment,
canEditComment,
canDeleteComment
}
}

View File

@@ -0,0 +1,8 @@
export default defineNuxtRouteMiddleware((to) => {
const { canWriteApplicationForms } = usePermissions()
if (to.path === '/create' && !canWriteApplicationForms.value) {
return navigateTo('/', { replace: true })
}
})

View File

@@ -16,18 +16,11 @@
<template #body> <template #body>
<div class="flex flex-col gap-4 sm:gap-6 lg:gap-12 w-full lg:max-w-4xl mx-auto"> <div class="flex flex-col gap-4 sm:gap-6 lg:gap-12 w-full lg:max-w-4xl mx-auto">
<div v-if="!true" class="text-center py-12"> <div v-if="!canWriteApplicationForms" class="text-center py-12">
<UIcon name="i-lucide-shield-x" class="w-16 h-16 mx-auto text-red-400 mb-4" /> <UIcon name="i-lucide-shield-x" class="w-16 h-16 mx-auto text-red-400 mb-4" />
<h2 class="text-2xl font-semibold text-gray-700 mb-2">Keine Berechtigung</h2> <h2 class="text-2xl font-semibold text-gray-700 mb-2">Keine Berechtigung</h2>
<p class="text-gray-500 mb-4">Sie haben keine Berechtigung zum Erstellen von Anträgen.</p> <p class="text-gray-500 mb-4">Sie haben keine Berechtigung zum Erstellen von Anträgen.</p>
<UAlert <UButton to="/" class="mt-4"> Zurück zur Übersicht </UButton>
v-if="currentRoleInfo"
:title="`Ihre aktuelle Rolle: ${currentRoleInfo.name}`"
:description="currentRoleInfo.description"
:color="currentRoleInfo.color"
variant="soft"
class="max-w-md mx-auto"
/>
</div> </div>
<div v-else> <div v-else>
<UPageCard title="Ampelstatus" variant="naked" orientation="horizontal" class="mb-4"> <UPageCard title="Ampelstatus" variant="naked" orientation="horizontal" class="mb-4">
@@ -86,17 +79,11 @@ import type { StepperItem } from '@nuxt/ui'
const { getAllApplicationFormTemplates } = await useApplicationFormTemplate() const { getAllApplicationFormTemplates } = await useApplicationFormTemplate()
const { createApplicationForm, submitApplicationForm } = useApplicationForm() const { createApplicationForm, submitApplicationForm } = useApplicationForm()
const { validateFormElements, getHighestComplianceStatus } = useApplicationFormValidator() const { validateFormElements, getHighestComplianceStatus } = useApplicationFormValidator()
const { canWriteApplicationForms } = usePermissions()
const userStore = useUserStore() const userStore = useUserStore()
const { selectedOrganization } = storeToRefs(userStore) const { selectedOrganization } = storeToRefs(userStore)
const toast = useToast() const toast = useToast()
// Get current role information for display
const currentRoleInfo = {
name: 'Mitarbeiter',
description: 'Sie können Anträge erstellen und bearbeiten.',
color: 'info'
}
const stepper = useTemplateRef('stepper') const stepper = useTemplateRef('stepper')
const activeStepperItemIndex = ref<number>(0) const activeStepperItemIndex = ref<number>(0)

View File

@@ -123,15 +123,18 @@ const selectedOrganizationId = computed({
} }
}) })
const items = [ const { canWriteApplicationForms } = usePermissions()
const items = computed(() => [
[ [
{ {
label: 'Neuer Mitbestimmungsantrag', label: 'Neuer Mitbestimmungsantrag',
icon: 'i-lucide-send', icon: 'i-lucide-send',
to: '/create' to: '/create',
disabled: !canWriteApplicationForms.value
} }
] ]
] ])
const applicationForms = computed({ const applicationForms = computed({
get: () => data?.value?.content ?? [], get: () => data?.value?.content ?? [],
@@ -147,12 +150,14 @@ function getLinksForApplicationForm(applicationForm: ApplicationFormDto) {
{ {
label: 'Bearbeiten', label: 'Bearbeiten',
icon: 'i-lucide-file-pen', icon: 'i-lucide-file-pen',
to: `/application-forms/${applicationForm.id}` to: `/application-forms/${applicationForm.id}`,
disabled: !canWriteApplicationForms.value
}, },
{ {
label: 'Löschen', label: 'Löschen',
icon: 'i-lucide-trash', icon: 'i-lucide-trash',
to: `?delete&id=${applicationForm.id}` to: `?delete&id=${applicationForm.id}`,
disabled: !canWriteApplicationForms.value
} }
] ]
} }

View File

@@ -13,12 +13,14 @@ export default defineOAuthKeycloakEventHandler({
} }
const organizations = decodedJwt ? extractOrganizations(decodedJwt) : [] const organizations = decodedJwt ? extractOrganizations(decodedJwt) : []
const roles = decodedJwt ? extractRoles(decodedJwt) : []
await setUserSession(event, { await setUserSession(event, {
user: { user: {
keycloakId: user.sub, keycloakId: user.sub,
name: user.preferred_username, name: user.preferred_username,
organizations organizations,
roles
}, },
jwt: { jwt: {
accessToken: tokens.access_token, accessToken: tokens.access_token,
@@ -54,3 +56,7 @@ function extractOrganizations(decoded: KeycloakTokenPayload): Organization[] {
return organizations return organizations
} }
function extractRoles(decoded: KeycloakTokenPayload): string[] {
return decoded?.resource_access?.legalconsenthub?.roles ?? []
}

View File

@@ -3,11 +3,13 @@ declare module '#auth-utils' {
keycloakId: string keycloakId: string
name: string name: string
organizations: Organization[] organizations: Organization[]
roles: string[]
} }
interface UserSession { interface UserSession {
name: string name: string
organizations: Organization[] organizations: Organization[]
roles: string[]
loggedInAt: number loggedInAt: number
jwt: { jwt: {
accessToken: string accessToken: string

View File

@@ -5,6 +5,11 @@ export interface KeycloakTokenPayload {
family_name?: string family_name?: string
email?: string email?: string
organization?: Record<string, { id?: string }> organization?: Record<string, { id?: string }>
resource_access?: {
legalconsenthub?: {
roles?: string[]
}
}
} }
export interface Organization { export interface Organization {