fixed permission implementation for ressources
This commit is contained in:
86
frontend/src/hooks/usePermissions.js
Normal file
86
frontend/src/hooks/usePermissions.js
Normal file
@@ -0,0 +1,86 @@
|
||||
import { useState, useEffect, useCallback } from 'react'
|
||||
import { useAuth } from '../contexts/AuthContext'
|
||||
|
||||
export const usePermissions = () => {
|
||||
const { authFetch, isAuthenticated } = useAuth()
|
||||
const [permissions, setPermissions] = useState({
|
||||
hasFullAccess: false,
|
||||
accessibleSpaces: [],
|
||||
canCreateSpace: false,
|
||||
canDeleteSpace: false,
|
||||
canCreateFqdn: {},
|
||||
canDeleteFqdn: {},
|
||||
canUploadCSR: {},
|
||||
canSignCSR: {},
|
||||
})
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
const fetchPermissions = useCallback(async () => {
|
||||
if (!isAuthenticated) {
|
||||
setLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
setLoading(true)
|
||||
const response = await authFetch('/api/user/permissions')
|
||||
if (response.ok) {
|
||||
const data = await response.json()
|
||||
setPermissions({
|
||||
hasFullAccess: data.hasFullAccess || false,
|
||||
accessibleSpaces: data.accessibleSpaces || [],
|
||||
canCreateSpace: data.permissions?.canCreateSpace || false,
|
||||
canDeleteSpace: data.permissions?.canDeleteSpace || false,
|
||||
canCreateFqdn: data.permissions?.canCreateFqdn || {},
|
||||
canDeleteFqdn: data.permissions?.canDeleteFqdn || {},
|
||||
canUploadCSR: data.permissions?.canUploadCSR || {},
|
||||
canSignCSR: data.permissions?.canSignCSR || {},
|
||||
})
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error fetching permissions:', err)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}, [isAuthenticated, authFetch])
|
||||
|
||||
useEffect(() => {
|
||||
if (isAuthenticated) {
|
||||
fetchPermissions()
|
||||
} else {
|
||||
setPermissions({
|
||||
hasFullAccess: false,
|
||||
accessibleSpaces: [],
|
||||
canCreateSpace: false,
|
||||
canDeleteSpace: false,
|
||||
canCreateFqdn: {},
|
||||
canDeleteFqdn: {},
|
||||
canUploadCSR: {},
|
||||
canSignCSR: {},
|
||||
})
|
||||
setLoading(false)
|
||||
}
|
||||
}, [isAuthenticated, fetchPermissions])
|
||||
|
||||
const canCreateSpace = () => permissions.canCreateSpace
|
||||
const canDeleteSpace = (spaceId) => permissions.canDeleteSpace
|
||||
const canCreateFqdn = (spaceId) => permissions.canCreateFqdn[spaceId] === true
|
||||
const canDeleteFqdn = (spaceId) => permissions.canDeleteFqdn[spaceId] === true
|
||||
const canUploadCSR = (spaceId) => permissions.canUploadCSR[spaceId] === true
|
||||
const canSignCSR = (spaceId) => permissions.canSignCSR[spaceId] === true
|
||||
const hasAccessToSpace = (spaceId) => permissions.accessibleSpaces.includes(spaceId)
|
||||
|
||||
return {
|
||||
permissions,
|
||||
loading,
|
||||
refreshPermissions: fetchPermissions,
|
||||
canCreateSpace,
|
||||
canDeleteSpace,
|
||||
canCreateFqdn,
|
||||
canDeleteFqdn,
|
||||
canUploadCSR,
|
||||
canSignCSR,
|
||||
hasAccessToSpace,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useParams, useNavigate } from 'react-router-dom'
|
||||
import { useAuth } from '../contexts/AuthContext'
|
||||
import { usePermissions } from '../hooks/usePermissions'
|
||||
|
||||
const SpaceDetail = () => {
|
||||
const { id } = useParams()
|
||||
const navigate = useNavigate()
|
||||
const { authFetch } = useAuth()
|
||||
const { canCreateFqdn, canDeleteFqdn, canSignCSR, canUploadCSR, refreshPermissions } = usePermissions()
|
||||
const [space, setSpace] = useState(null)
|
||||
const [fqdns, setFqdns] = useState([])
|
||||
const [showForm, setShowForm] = useState(false)
|
||||
@@ -123,6 +125,8 @@ const SpaceDetail = () => {
|
||||
setFqdns([...fqdns, newFqdn])
|
||||
setFormData({ fqdn: '', description: '' })
|
||||
setShowForm(false)
|
||||
// Aktualisiere Berechtigungen nach dem Erstellen eines FQDNs
|
||||
refreshPermissions()
|
||||
} else {
|
||||
let errorMessage = 'Fehler beim Erstellen des FQDN'
|
||||
try {
|
||||
@@ -176,6 +180,8 @@ const SpaceDetail = () => {
|
||||
setShowDeleteModal(false)
|
||||
setFqdnToDelete(null)
|
||||
setConfirmChecked(false)
|
||||
// Aktualisiere Berechtigungen nach dem Löschen eines FQDNs
|
||||
refreshPermissions()
|
||||
} else {
|
||||
const errorData = await response.json().catch(() => ({ error: 'Fehler beim Löschen' }))
|
||||
alert(errorData.error || 'Fehler beim Löschen des FQDN')
|
||||
@@ -586,7 +592,13 @@ const SpaceDetail = () => {
|
||||
</h3>
|
||||
<button
|
||||
onClick={() => setShowForm(!showForm)}
|
||||
className="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white font-semibold rounded-lg transition-all duration-200"
|
||||
disabled={!canCreateFqdn(id) && !showForm}
|
||||
className={`px-4 py-2 font-semibold rounded-lg transition-all duration-200 ${
|
||||
canCreateFqdn(id) || showForm
|
||||
? 'bg-blue-600 hover:bg-blue-700 text-white'
|
||||
: 'bg-slate-600 text-slate-400 cursor-not-allowed opacity-50'
|
||||
}`}
|
||||
title={!canCreateFqdn(id) && !showForm ? 'Keine Berechtigung zum Erstellen von FQDNs' : ''}
|
||||
>
|
||||
{showForm ? 'Abbrechen' : '+ Neuer FQDN'}
|
||||
</button>
|
||||
@@ -630,11 +642,18 @@ const SpaceDetail = () => {
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
handleRequestSigning(fqdn)
|
||||
if (canSignCSR(id)) {
|
||||
handleRequestSigning(fqdn)
|
||||
}
|
||||
}}
|
||||
className="p-2 text-purple-400 hover:text-purple-300 hover:bg-purple-500/20 rounded-lg transition-colors"
|
||||
title="CSR signieren lassen"
|
||||
aria-label="CSR signieren lassen"
|
||||
disabled={!canSignCSR(id)}
|
||||
className={`p-2 rounded-lg transition-colors ${
|
||||
canSignCSR(id)
|
||||
? 'text-purple-400 hover:text-purple-300 hover:bg-purple-500/20'
|
||||
: 'text-slate-500 cursor-not-allowed opacity-50'
|
||||
}`}
|
||||
title={canSignCSR(id) ? 'CSR signieren lassen' : 'Keine Berechtigung zum Signieren von CSRs'}
|
||||
aria-label={canSignCSR(id) ? 'CSR signieren lassen' : 'Keine Berechtigung'}
|
||||
>
|
||||
<svg
|
||||
className="w-5 h-5"
|
||||
@@ -692,16 +711,23 @@ const SpaceDetail = () => {
|
||||
}
|
||||
e.target.value = ''
|
||||
}}
|
||||
disabled={uploadingCSR}
|
||||
disabled={uploadingCSR || !canUploadCSR(id)}
|
||||
/>
|
||||
<button
|
||||
className="p-2 text-blue-400 hover:text-blue-300 hover:bg-blue-500/20 rounded-lg transition-colors"
|
||||
title="CSR hochladen"
|
||||
aria-label="CSR hochladen"
|
||||
disabled={!canUploadCSR(id)}
|
||||
className={`p-2 rounded-lg transition-colors ${
|
||||
canUploadCSR(id)
|
||||
? 'text-blue-400 hover:text-blue-300 hover:bg-blue-500/20'
|
||||
: 'text-slate-500 cursor-not-allowed opacity-50'
|
||||
}`}
|
||||
title={canUploadCSR(id) ? 'CSR hochladen' : 'Keine Berechtigung zum Hochladen von CSRs'}
|
||||
aria-label={canUploadCSR(id) ? 'CSR hochladen' : 'Keine Berechtigung'}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
e.currentTarget.parentElement.querySelector('input[type="file"]')?.click()
|
||||
if (canUploadCSR(id)) {
|
||||
e.currentTarget.parentElement.querySelector('input[type="file"]')?.click()
|
||||
}
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
@@ -806,11 +832,18 @@ const SpaceDetail = () => {
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
handleDelete(fqdn)
|
||||
if (canDeleteFqdn(id)) {
|
||||
handleDelete(fqdn)
|
||||
}
|
||||
}}
|
||||
className="p-2 text-red-400 hover:text-red-300 hover:bg-red-500/20 rounded-lg transition-colors"
|
||||
title="FQDN löschen"
|
||||
aria-label="FQDN löschen"
|
||||
disabled={!canDeleteFqdn(id)}
|
||||
className={`p-2 rounded-lg transition-colors ${
|
||||
canDeleteFqdn(id)
|
||||
? 'text-red-400 hover:text-red-300 hover:bg-red-500/20'
|
||||
: 'text-slate-500 cursor-not-allowed opacity-50'
|
||||
}`}
|
||||
title={canDeleteFqdn(id) ? 'FQDN löschen' : 'Keine Berechtigung zum Löschen von FQDNs'}
|
||||
aria-label={canDeleteFqdn(id) ? 'FQDN löschen' : 'Keine Berechtigung'}
|
||||
>
|
||||
<svg
|
||||
className="w-5 h-5"
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { useAuth } from '../contexts/AuthContext'
|
||||
import { usePermissions } from '../hooks/usePermissions'
|
||||
|
||||
const Spaces = () => {
|
||||
const navigate = useNavigate()
|
||||
const { authFetch } = useAuth()
|
||||
const { canCreateSpace, canDeleteSpace, refreshPermissions } = usePermissions()
|
||||
const [spaces, setSpaces] = useState([])
|
||||
const [showForm, setShowForm] = useState(false)
|
||||
const [formData, setFormData] = useState({
|
||||
@@ -72,6 +74,8 @@ const Spaces = () => {
|
||||
setSpaces([...spaces, newSpace])
|
||||
setFormData({ name: '', description: '' })
|
||||
setShowForm(false)
|
||||
// Aktualisiere Berechtigungen nach dem Erstellen eines Spaces
|
||||
refreshPermissions()
|
||||
} else {
|
||||
const errorData = await response.json()
|
||||
setError(errorData.error || 'Fehler beim Erstellen des Space')
|
||||
@@ -140,6 +144,8 @@ const Spaces = () => {
|
||||
setConfirmChecked(false)
|
||||
setDeleteFqdnsChecked(false)
|
||||
setFqdnCount(0)
|
||||
// Aktualisiere Berechtigungen nach dem Löschen eines Spaces
|
||||
refreshPermissions()
|
||||
} else {
|
||||
const errorText = await response.text()
|
||||
let errorMessage = 'Fehler beim Löschen des Space'
|
||||
@@ -188,7 +194,13 @@ const Spaces = () => {
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setShowForm(!showForm)}
|
||||
className="px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white font-semibold rounded-lg shadow-lg hover:shadow-xl transition-all duration-200"
|
||||
disabled={!canCreateSpace() && !showForm}
|
||||
className={`px-6 py-3 font-semibold rounded-lg shadow-lg transition-all duration-200 ${
|
||||
canCreateSpace() || showForm
|
||||
? 'bg-blue-600 hover:bg-blue-700 text-white hover:shadow-xl'
|
||||
: 'bg-slate-600 text-slate-400 cursor-not-allowed opacity-50'
|
||||
}`}
|
||||
title={!canCreateSpace() && !showForm ? 'Keine Berechtigung zum Erstellen von Spaces' : ''}
|
||||
>
|
||||
{showForm ? 'Abbrechen' : '+ Neuer Space'}
|
||||
</button>
|
||||
@@ -340,11 +352,18 @@ const Spaces = () => {
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
handleDelete(space)
|
||||
if (canDeleteSpace(space.id)) {
|
||||
handleDelete(space)
|
||||
}
|
||||
}}
|
||||
className="p-2 text-red-400 hover:text-red-300 hover:bg-red-500/20 rounded-lg transition-colors"
|
||||
title="Space löschen"
|
||||
aria-label="Space löschen"
|
||||
disabled={!canDeleteSpace(space.id)}
|
||||
className={`p-2 rounded-lg transition-colors ${
|
||||
canDeleteSpace(space.id)
|
||||
? 'text-red-400 hover:text-red-300 hover:bg-red-500/20'
|
||||
: 'text-slate-500 cursor-not-allowed opacity-50'
|
||||
}`}
|
||||
title={canDeleteSpace(space.id) ? 'Space löschen' : 'Keine Berechtigung zum Löschen von Spaces'}
|
||||
aria-label={canDeleteSpace(space.id) ? 'Space löschen' : 'Keine Berechtigung'}
|
||||
>
|
||||
<svg
|
||||
className="w-5 h-5"
|
||||
|
||||
Reference in New Issue
Block a user