Compare commits
2 Commits
5523c6ff06
...
d23bfa0376
| Author | SHA1 | Date | |
|---|---|---|---|
| d23bfa0376 | |||
| 0d17fda341 |
735
backend/main.go
735
backend/main.go
@@ -823,16 +823,35 @@ func getSpacesHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verwende Prepared Statement für bessere Performance und Sicherheit
|
// Hole Benutzer-ID
|
||||||
stmt, err := db.Prepare("SELECT id, name, description, created_at FROM spaces ORDER BY created_at DESC")
|
userID, _ := getUserFromRequest(r)
|
||||||
|
|
||||||
|
// Hole alle Spaces, auf die der Benutzer Zugriff hat
|
||||||
|
accessibleSpaceIDs, err := getAccessibleSpaceIDs(userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "Fehler beim Vorbereiten der Abfrage", http.StatusInternalServerError)
|
http.Error(w, "Fehler beim Prüfen der Berechtigungen", http.StatusInternalServerError)
|
||||||
log.Printf("Fehler beim Vorbereiten der Abfrage: %v", err)
|
log.Printf("Fehler beim Prüfen der Berechtigungen: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer stmt.Close()
|
|
||||||
|
|
||||||
rows, err := stmt.Query()
|
// Wenn der Benutzer keinen Zugriff auf Spaces hat, gebe leeres Array zurück
|
||||||
|
if len(accessibleSpaceIDs) == 0 {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
json.NewEncoder(w).Encode([]Space{})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Baue Query mit IN-Klausel für die zugänglichen Spaces
|
||||||
|
placeholders := make([]string, len(accessibleSpaceIDs))
|
||||||
|
args := make([]interface{}, len(accessibleSpaceIDs))
|
||||||
|
for i, spaceID := range accessibleSpaceIDs {
|
||||||
|
placeholders[i] = "?"
|
||||||
|
args[i] = spaceID
|
||||||
|
}
|
||||||
|
|
||||||
|
query := fmt.Sprintf("SELECT id, name, description, created_at FROM spaces WHERE id IN (%s) ORDER BY created_at DESC", strings.Join(placeholders, ","))
|
||||||
|
|
||||||
|
rows, err := db.Query(query, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "Fehler beim Abrufen der Spaces", http.StatusInternalServerError)
|
http.Error(w, "Fehler beim Abrufen der Spaces", http.StatusInternalServerError)
|
||||||
log.Printf("Fehler beim Abrufen der Spaces: %v", err)
|
log.Printf("Fehler beim Abrufen der Spaces: %v", err)
|
||||||
@@ -888,6 +907,33 @@ func createSpaceHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prüfe Berechtigung: Nur FULL_ACCESS darf Spaces erstellen
|
||||||
|
userID, username := getUserFromRequest(r)
|
||||||
|
if userID == "" {
|
||||||
|
http.Error(w, "Nicht authentifiziert", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prüfe, ob der Benutzer FULL_ACCESS hat (ohne Space-Beschränkung)
|
||||||
|
permissions, err := getUserPermissions(userID)
|
||||||
|
if err != nil || len(permissions.Groups) == 0 {
|
||||||
|
http.Error(w, "Keine Berechtigung zum Erstellen von Spaces", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hasFullAccess := false
|
||||||
|
for _, group := range permissions.Groups {
|
||||||
|
if group.Permission == PermissionFullAccess {
|
||||||
|
hasFullAccess = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasFullAccess {
|
||||||
|
http.Error(w, "Keine Berechtigung zum Erstellen von Spaces. Vollzugriff erforderlich.", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var req CreateSpaceRequest
|
var req CreateSpaceRequest
|
||||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||||
@@ -904,7 +950,7 @@ func createSpaceHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
createdAt := time.Now()
|
createdAt := time.Now()
|
||||||
|
|
||||||
// Speichere in Datenbank
|
// Speichere in Datenbank
|
||||||
_, err := db.Exec(
|
_, err = db.Exec(
|
||||||
"INSERT INTO spaces (id, name, description, created_at) VALUES (?, ?, ?, ?)",
|
"INSERT INTO spaces (id, name, description, created_at) VALUES (?, ?, ?, ?)",
|
||||||
id, req.Name, req.Description, createdAt,
|
id, req.Name, req.Description, createdAt,
|
||||||
)
|
)
|
||||||
@@ -926,7 +972,6 @@ func createSpaceHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// Audit-Log: Space erstellt
|
// Audit-Log: Space erstellt
|
||||||
if auditService != nil {
|
if auditService != nil {
|
||||||
userID, username := getUserFromRequest(r)
|
|
||||||
ipAddress, userAgent := getRequestInfo(r)
|
ipAddress, userAgent := getRequestInfo(r)
|
||||||
auditService.Track(r.Context(), "CREATE", "space", id, userID, username, map[string]interface{}{
|
auditService.Track(r.Context(), "CREATE", "space", id, userID, username, map[string]interface{}{
|
||||||
"name": req.Name,
|
"name": req.Name,
|
||||||
@@ -955,9 +1000,28 @@ func deleteSpaceHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prüfe Berechtigung: Nur FULL_ACCESS darf Spaces löschen
|
||||||
|
userID, username := getUserFromRequest(r)
|
||||||
|
if userID == "" {
|
||||||
|
http.Error(w, "Nicht authentifiziert", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hasPermission, err := hasPermission(userID, id, PermissionFullAccess)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Fehler beim Prüfen der Berechtigung", http.StatusInternalServerError)
|
||||||
|
log.Printf("Fehler beim Prüfen der Berechtigung: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasPermission {
|
||||||
|
http.Error(w, "Keine Berechtigung zum Löschen von Spaces. Vollzugriff erforderlich.", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Prüfe ob der Space existiert
|
// Prüfe ob der Space existiert
|
||||||
var exists bool
|
var exists bool
|
||||||
err := db.QueryRow("SELECT EXISTS(SELECT 1 FROM spaces WHERE id = ?)", id).Scan(&exists)
|
err = db.QueryRow("SELECT EXISTS(SELECT 1 FROM spaces WHERE id = ?)", id).Scan(&exists)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "Fehler beim Prüfen des Space", http.StatusInternalServerError)
|
http.Error(w, "Fehler beim Prüfen des Space", http.StatusInternalServerError)
|
||||||
log.Printf("Fehler beim Prüfen des Space: %v", err)
|
log.Printf("Fehler beim Prüfen des Space: %v", err)
|
||||||
@@ -1040,7 +1104,6 @@ func deleteSpaceHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
json.NewEncoder(w).Encode(map[string]string{"message": "Space erfolgreich gelöscht"})
|
json.NewEncoder(w).Encode(map[string]string{"message": "Space erfolgreich gelöscht"})
|
||||||
|
|
||||||
// Audit-Log: Space gelöscht
|
// Audit-Log: Space gelöscht
|
||||||
userID, username := getUserFromRequest(r)
|
|
||||||
ipAddress, userAgent := getRequestInfo(r)
|
ipAddress, userAgent := getRequestInfo(r)
|
||||||
details := map[string]interface{}{
|
details := map[string]interface{}{
|
||||||
"message": fmt.Sprintf("Space gelöscht: %s", id),
|
"message": fmt.Sprintf("Space gelöscht: %s", id),
|
||||||
@@ -1071,8 +1134,27 @@ func getSpaceFqdnCountHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prüfe Berechtigung: Benutzer muss Zugriff auf den Space haben
|
||||||
|
userID, _ := getUserFromRequest(r)
|
||||||
|
if userID == "" {
|
||||||
|
http.Error(w, "Nicht authentifiziert", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hasAccess, err := hasSpaceAccess(userID, spaceID)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Fehler beim Prüfen der Berechtigung", http.StatusInternalServerError)
|
||||||
|
log.Printf("Fehler beim Prüfen der Berechtigung: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasAccess {
|
||||||
|
http.Error(w, "Keine Berechtigung für diesen Space", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var count int
|
var count int
|
||||||
err := db.QueryRow("SELECT COUNT(*) FROM fqdns WHERE space_id = ?", spaceID).Scan(&count)
|
err = db.QueryRow("SELECT COUNT(*) FROM fqdns WHERE space_id = ?", spaceID).Scan(&count)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "Fehler beim Abrufen der FQDN-Anzahl", http.StatusInternalServerError)
|
http.Error(w, "Fehler beim Abrufen der FQDN-Anzahl", http.StatusInternalServerError)
|
||||||
log.Printf("Fehler beim Abrufen der FQDN-Anzahl: %v", err)
|
log.Printf("Fehler beim Abrufen der FQDN-Anzahl: %v", err)
|
||||||
@@ -1101,9 +1183,28 @@ func getFqdnsHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prüfe Berechtigung: Benutzer muss Zugriff auf den Space haben
|
||||||
|
userID, _ := getUserFromRequest(r)
|
||||||
|
if userID == "" {
|
||||||
|
http.Error(w, "Nicht authentifiziert", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hasAccess, err := hasSpaceAccess(userID, spaceID)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Fehler beim Prüfen der Berechtigung", http.StatusInternalServerError)
|
||||||
|
log.Printf("Fehler beim Prüfen der Berechtigung: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasAccess {
|
||||||
|
http.Error(w, "Keine Berechtigung für diesen Space", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Prüfe ob der Space existiert
|
// Prüfe ob der Space existiert
|
||||||
var exists bool
|
var exists bool
|
||||||
err := db.QueryRow("SELECT EXISTS(SELECT 1 FROM spaces WHERE id = ?)", spaceID).Scan(&exists)
|
err = db.QueryRow("SELECT EXISTS(SELECT 1 FROM spaces WHERE id = ?)", spaceID).Scan(&exists)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "Fehler beim Prüfen des Space", http.StatusInternalServerError)
|
http.Error(w, "Fehler beim Prüfen des Space", http.StatusInternalServerError)
|
||||||
log.Printf("Fehler beim Prüfen des Space: %v", err)
|
log.Printf("Fehler beim Prüfen des Space: %v", err)
|
||||||
@@ -1175,9 +1276,28 @@ func createFqdnHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prüfe Berechtigung: READ_WRITE oder FULL_ACCESS erforderlich zum Erstellen von FQDNs
|
||||||
|
userID, username := getUserFromRequest(r)
|
||||||
|
if userID == "" {
|
||||||
|
http.Error(w, "Nicht authentifiziert", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hasPermission, err := hasPermission(userID, spaceID, PermissionReadWrite)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Fehler beim Prüfen der Berechtigung", http.StatusInternalServerError)
|
||||||
|
log.Printf("Fehler beim Prüfen der Berechtigung: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasPermission {
|
||||||
|
http.Error(w, "Keine Berechtigung zum Erstellen von FQDNs. Lesen/Schreiben oder Vollzugriff erforderlich.", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Prüfe ob der Space existiert
|
// Prüfe ob der Space existiert
|
||||||
var exists bool
|
var exists bool
|
||||||
err := db.QueryRow("SELECT EXISTS(SELECT 1 FROM spaces WHERE id = ?)", spaceID).Scan(&exists)
|
err = db.QueryRow("SELECT EXISTS(SELECT 1 FROM spaces WHERE id = ?)", spaceID).Scan(&exists)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "Fehler beim Prüfen des Space", http.StatusInternalServerError)
|
http.Error(w, "Fehler beim Prüfen des Space", http.StatusInternalServerError)
|
||||||
log.Printf("Fehler beim Prüfen des Space: %v", err)
|
log.Printf("Fehler beim Prüfen des Space: %v", err)
|
||||||
@@ -1241,7 +1361,6 @@ func createFqdnHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
json.NewEncoder(w).Encode(newFqdn)
|
json.NewEncoder(w).Encode(newFqdn)
|
||||||
|
|
||||||
// Audit-Log: FQDN erstellt
|
// Audit-Log: FQDN erstellt
|
||||||
userID, username := getUserFromRequest(r)
|
|
||||||
ipAddress, userAgent := getRequestInfo(r)
|
ipAddress, userAgent := getRequestInfo(r)
|
||||||
auditService.Track(r.Context(), "CREATE", "fqdn", id, userID, username, map[string]interface{}{
|
auditService.Track(r.Context(), "CREATE", "fqdn", id, userID, username, map[string]interface{}{
|
||||||
"fqdn": req.FQDN,
|
"fqdn": req.FQDN,
|
||||||
@@ -1271,9 +1390,28 @@ func deleteFqdnHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prüfe Berechtigung: Nur FULL_ACCESS darf FQDNs löschen
|
||||||
|
userID, username := getUserFromRequest(r)
|
||||||
|
if userID == "" {
|
||||||
|
http.Error(w, "Nicht authentifiziert", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hasPermission, err := hasPermission(userID, spaceID, PermissionFullAccess)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Fehler beim Prüfen der Berechtigung", http.StatusInternalServerError)
|
||||||
|
log.Printf("Fehler beim Prüfen der Berechtigung: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasPermission {
|
||||||
|
http.Error(w, "Keine Berechtigung zum Löschen von FQDNs. Vollzugriff erforderlich.", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Prüfe ob der FQDN existiert und zum Space gehört
|
// Prüfe ob der FQDN existiert und zum Space gehört
|
||||||
var exists bool
|
var exists bool
|
||||||
err := db.QueryRow("SELECT EXISTS(SELECT 1 FROM fqdns WHERE id = ? AND space_id = ?)", fqdnID, spaceID).Scan(&exists)
|
err = db.QueryRow("SELECT EXISTS(SELECT 1 FROM fqdns WHERE id = ? AND space_id = ?)", fqdnID, spaceID).Scan(&exists)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "Fehler beim Prüfen des FQDN", http.StatusInternalServerError)
|
http.Error(w, "Fehler beim Prüfen des FQDN", http.StatusInternalServerError)
|
||||||
log.Printf("Fehler beim Prüfen des FQDN: %v", err)
|
log.Printf("Fehler beim Prüfen des FQDN: %v", err)
|
||||||
@@ -1337,7 +1475,6 @@ func deleteFqdnHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
json.NewEncoder(w).Encode(map[string]string{"message": "FQDN erfolgreich gelöscht"})
|
json.NewEncoder(w).Encode(map[string]string{"message": "FQDN erfolgreich gelöscht"})
|
||||||
|
|
||||||
// Audit-Log: FQDN gelöscht
|
// Audit-Log: FQDN gelöscht
|
||||||
userID, username := getUserFromRequest(r)
|
|
||||||
ipAddress, userAgent := getRequestInfo(r)
|
ipAddress, userAgent := getRequestInfo(r)
|
||||||
auditService.Track(r.Context(), "DELETE", "fqdn", fqdnID, userID, username, map[string]interface{}{
|
auditService.Track(r.Context(), "DELETE", "fqdn", fqdnID, userID, username, map[string]interface{}{
|
||||||
"spaceId": spaceID,
|
"spaceId": spaceID,
|
||||||
@@ -1364,9 +1501,28 @@ func deleteAllFqdnsHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prüfe Berechtigung: Nur FULL_ACCESS darf alle FQDNs eines Spaces löschen
|
||||||
|
userID, _ := getUserFromRequest(r)
|
||||||
|
if userID == "" {
|
||||||
|
http.Error(w, "Nicht authentifiziert", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hasPermission, err := hasPermission(userID, spaceID, PermissionFullAccess)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Fehler beim Prüfen der Berechtigung", http.StatusInternalServerError)
|
||||||
|
log.Printf("Fehler beim Prüfen der Berechtigung: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasPermission {
|
||||||
|
http.Error(w, "Keine Berechtigung zum Löschen aller FQDNs. Vollzugriff erforderlich.", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Prüfe ob der Space existiert
|
// Prüfe ob der Space existiert
|
||||||
var exists bool
|
var exists bool
|
||||||
err := db.QueryRow("SELECT EXISTS(SELECT 1 FROM spaces WHERE id = ?)", spaceID).Scan(&exists)
|
err = db.QueryRow("SELECT EXISTS(SELECT 1 FROM spaces WHERE id = ?)", spaceID).Scan(&exists)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "Fehler beim Prüfen des Space", http.StatusInternalServerError)
|
http.Error(w, "Fehler beim Prüfen des Space", http.StatusInternalServerError)
|
||||||
log.Printf("Fehler beim Prüfen des Space: %v", err)
|
log.Printf("Fehler beim Prüfen des Space: %v", err)
|
||||||
@@ -1438,6 +1594,32 @@ func deleteAllFqdnsGlobalHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prüfe Berechtigung: Nur FULL_ACCESS darf alle FQDNs global löschen
|
||||||
|
userID, _ := getUserFromRequest(r)
|
||||||
|
if userID == "" {
|
||||||
|
http.Error(w, "Nicht authentifiziert", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
permissions, err := getUserPermissions(userID)
|
||||||
|
if err != nil || len(permissions.Groups) == 0 {
|
||||||
|
http.Error(w, "Keine Berechtigung zum Löschen aller FQDNs. Vollzugriff erforderlich.", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hasFullAccess := false
|
||||||
|
for _, group := range permissions.Groups {
|
||||||
|
if group.Permission == PermissionFullAccess {
|
||||||
|
hasFullAccess = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasFullAccess {
|
||||||
|
http.Error(w, "Keine Berechtigung zum Löschen aller FQDNs. Vollzugriff erforderlich.", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Prüfe Query-Parameter für Bestätigung (Sicherheitsmaßnahme)
|
// Prüfe Query-Parameter für Bestätigung (Sicherheitsmaßnahme)
|
||||||
confirm := r.URL.Query().Get("confirm")
|
confirm := r.URL.Query().Get("confirm")
|
||||||
if confirm != "true" {
|
if confirm != "true" {
|
||||||
@@ -1447,7 +1629,7 @@ func deleteAllFqdnsGlobalHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// Zähle zuerst die Anzahl der FQDNs
|
// Zähle zuerst die Anzahl der FQDNs
|
||||||
var totalCount int
|
var totalCount int
|
||||||
err := db.QueryRow("SELECT COUNT(*) FROM fqdns").Scan(&totalCount)
|
err = db.QueryRow("SELECT COUNT(*) FROM fqdns").Scan(&totalCount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "Fehler beim Zählen der FQDNs", http.StatusInternalServerError)
|
http.Error(w, "Fehler beim Zählen der FQDNs", http.StatusInternalServerError)
|
||||||
log.Printf("Fehler beim Zählen der FQDNs: %v", err)
|
log.Printf("Fehler beim Zählen der FQDNs: %v", err)
|
||||||
@@ -1523,6 +1705,32 @@ func deleteAllCSRsHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prüfe Berechtigung: Nur FULL_ACCESS darf alle CSRs löschen
|
||||||
|
userID, _ := getUserFromRequest(r)
|
||||||
|
if userID == "" {
|
||||||
|
http.Error(w, "Nicht authentifiziert", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
permissions, err := getUserPermissions(userID)
|
||||||
|
if err != nil || len(permissions.Groups) == 0 {
|
||||||
|
http.Error(w, "Keine Berechtigung zum Löschen aller CSRs. Vollzugriff erforderlich.", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hasFullAccess := false
|
||||||
|
for _, group := range permissions.Groups {
|
||||||
|
if group.Permission == PermissionFullAccess {
|
||||||
|
hasFullAccess = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasFullAccess {
|
||||||
|
http.Error(w, "Keine Berechtigung zum Löschen aller CSRs. Vollzugriff erforderlich.", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Prüfe Query-Parameter für Bestätigung (Sicherheitsmaßnahme)
|
// Prüfe Query-Parameter für Bestätigung (Sicherheitsmaßnahme)
|
||||||
confirm := r.URL.Query().Get("confirm")
|
confirm := r.URL.Query().Get("confirm")
|
||||||
if confirm != "true" {
|
if confirm != "true" {
|
||||||
@@ -1532,7 +1740,7 @@ func deleteAllCSRsHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// Zähle zuerst die Anzahl der CSRs
|
// Zähle zuerst die Anzahl der CSRs
|
||||||
var totalCount int
|
var totalCount int
|
||||||
err := db.QueryRow("SELECT COUNT(*) FROM csrs").Scan(&totalCount)
|
err = db.QueryRow("SELECT COUNT(*) FROM csrs").Scan(&totalCount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "Fehler beim Zählen der CSRs", http.StatusInternalServerError)
|
http.Error(w, "Fehler beim Zählen der CSRs", http.StatusInternalServerError)
|
||||||
log.Printf("Fehler beim Zählen der CSRs: %v", err)
|
log.Printf("Fehler beim Zählen der CSRs: %v", err)
|
||||||
@@ -1615,6 +1823,25 @@ func uploadCSRHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prüfe Berechtigung: READ_WRITE oder FULL_ACCESS erforderlich zum Hochladen von CSRs
|
||||||
|
userID, username := getUserFromRequest(r)
|
||||||
|
if userID == "" {
|
||||||
|
http.Error(w, "Nicht authentifiziert", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hasPermission, err := hasPermission(userID, spaceID, PermissionReadWrite)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Fehler beim Prüfen der Berechtigung", http.StatusInternalServerError)
|
||||||
|
log.Printf("Fehler beim Prüfen der Berechtigung: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasPermission {
|
||||||
|
http.Error(w, "Keine Berechtigung zum Hochladen von CSRs. Lesen/Schreiben oder Vollzugriff erforderlich.", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Prüfe ob Space existiert
|
// Prüfe ob Space existiert
|
||||||
var spaceExists bool
|
var spaceExists bool
|
||||||
err = db.QueryRow("SELECT EXISTS(SELECT 1 FROM spaces WHERE id = ?)", spaceID).Scan(&spaceExists)
|
err = db.QueryRow("SELECT EXISTS(SELECT 1 FROM spaces WHERE id = ?)", spaceID).Scan(&spaceExists)
|
||||||
@@ -1761,7 +1988,6 @@ func uploadCSRHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
json.NewEncoder(w).Encode(newCSR)
|
json.NewEncoder(w).Encode(newCSR)
|
||||||
|
|
||||||
// Audit-Log: CSR hochgeladen
|
// Audit-Log: CSR hochgeladen
|
||||||
userID, username := getUserFromRequest(r)
|
|
||||||
ipAddress, userAgent := getRequestInfo(r)
|
ipAddress, userAgent := getRequestInfo(r)
|
||||||
auditService.Track(r.Context(), "UPLOAD", "csr", csrID, userID, username, map[string]interface{}{
|
auditService.Track(r.Context(), "UPLOAD", "csr", csrID, userID, username, map[string]interface{}{
|
||||||
"fqdnId": fqdnID,
|
"fqdnId": fqdnID,
|
||||||
@@ -1790,6 +2016,25 @@ func getCSRByFQDNHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prüfe Berechtigung: Benutzer muss Zugriff auf den Space haben (READ, READ_WRITE oder FULL_ACCESS)
|
||||||
|
userID, _ := getUserFromRequest(r)
|
||||||
|
if userID == "" {
|
||||||
|
http.Error(w, "Nicht authentifiziert", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hasAccess, err := hasSpaceAccess(userID, spaceID)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Fehler beim Prüfen der Berechtigung", http.StatusInternalServerError)
|
||||||
|
log.Printf("Fehler beim Prüfen der Berechtigung: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasAccess {
|
||||||
|
http.Error(w, "Keine Berechtigung für diesen Space", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Prüfe ob nur der neueste CSR gewünscht ist
|
// Prüfe ob nur der neueste CSR gewünscht ist
|
||||||
latestOnly := r.URL.Query().Get("latest") == "true"
|
latestOnly := r.URL.Query().Get("latest") == "true"
|
||||||
|
|
||||||
@@ -2502,6 +2747,113 @@ components:
|
|||||||
|
|
||||||
// User Handler Functions
|
// User Handler Functions
|
||||||
|
|
||||||
|
func getUserPermissionsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||||
|
w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS")
|
||||||
|
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
|
||||||
|
|
||||||
|
if r.Method == "OPTIONS" {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hole Benutzer-ID
|
||||||
|
userID, _ := getUserFromRequest(r)
|
||||||
|
if userID == "" {
|
||||||
|
http.Error(w, "Nicht authentifiziert", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hole Berechtigungen
|
||||||
|
permissions, err := getUserPermissions(userID)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Fehler beim Abrufen der Berechtigungen", http.StatusInternalServerError)
|
||||||
|
log.Printf("Fehler beim Abrufen der Berechtigungen: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erstelle vereinfachte Antwort für Frontend
|
||||||
|
canCreateFqdn := make(map[string]bool)
|
||||||
|
canDeleteFqdn := make(map[string]bool)
|
||||||
|
canUploadCSR := make(map[string]bool)
|
||||||
|
canSignCSR := make(map[string]bool)
|
||||||
|
|
||||||
|
response := map[string]interface{}{
|
||||||
|
"userId": userID,
|
||||||
|
"hasFullAccess": permissions.HasFullAccess,
|
||||||
|
"accessibleSpaces": []string{},
|
||||||
|
"permissions": map[string]interface{}{
|
||||||
|
"canCreateSpace": false,
|
||||||
|
"canDeleteSpace": false,
|
||||||
|
"canCreateFqdn": canCreateFqdn,
|
||||||
|
"canDeleteFqdn": canDeleteFqdn,
|
||||||
|
"canUploadCSR": canUploadCSR,
|
||||||
|
"canSignCSR": canSignCSR,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hole alle Spaces
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
spaceRows, err := db.QueryContext(ctx, "SELECT id FROM spaces")
|
||||||
|
if err == nil {
|
||||||
|
defer spaceRows.Close()
|
||||||
|
var allSpaceIDs []string
|
||||||
|
for spaceRows.Next() {
|
||||||
|
var spaceID string
|
||||||
|
if err := spaceRows.Scan(&spaceID); err == nil {
|
||||||
|
allSpaceIDs = append(allSpaceIDs, spaceID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spaceRows.Close()
|
||||||
|
|
||||||
|
// Prüfe für jeden Space die Berechtigungen
|
||||||
|
accessibleSpaces := []string{}
|
||||||
|
|
||||||
|
for _, spaceID := range allSpaceIDs {
|
||||||
|
hasAccess, _ := hasSpaceAccess(userID, spaceID)
|
||||||
|
if hasAccess {
|
||||||
|
accessibleSpaces = append(accessibleSpaces, spaceID)
|
||||||
|
|
||||||
|
// Prüfe READ_WRITE für FQDN erstellen und CSR upload/sign
|
||||||
|
hasReadWrite, _ := hasPermission(userID, spaceID, PermissionReadWrite)
|
||||||
|
canCreateFqdn[spaceID] = hasReadWrite
|
||||||
|
canUploadCSR[spaceID] = hasReadWrite
|
||||||
|
canSignCSR[spaceID] = hasReadWrite
|
||||||
|
|
||||||
|
// Prüfe FULL_ACCESS für FQDN löschen
|
||||||
|
hasFullAccess, _ := hasPermission(userID, spaceID, PermissionFullAccess)
|
||||||
|
canDeleteFqdn[spaceID] = hasFullAccess
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response["accessibleSpaces"] = accessibleSpaces
|
||||||
|
perms := response["permissions"].(map[string]interface{})
|
||||||
|
perms["canCreateFqdn"] = canCreateFqdn
|
||||||
|
perms["canDeleteFqdn"] = canDeleteFqdn
|
||||||
|
perms["canUploadCSR"] = canUploadCSR
|
||||||
|
perms["canSignCSR"] = canSignCSR
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prüfe globale Berechtigungen (Space erstellen/löschen)
|
||||||
|
hasFullAccessGlobal := false
|
||||||
|
for _, group := range permissions.Groups {
|
||||||
|
if group.Permission == PermissionFullAccess {
|
||||||
|
hasFullAccessGlobal = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
perms := response["permissions"].(map[string]interface{})
|
||||||
|
perms["canCreateSpace"] = hasFullAccessGlobal
|
||||||
|
perms["canDeleteSpace"] = hasFullAccessGlobal
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
json.NewEncoder(w).Encode(response)
|
||||||
|
}
|
||||||
|
|
||||||
func getUsersHandler(w http.ResponseWriter, r *http.Request) {
|
func getUsersHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||||
@@ -3612,6 +3964,262 @@ func getUserFromRequest(r *http.Request) (userID, username string) {
|
|||||||
return id, username
|
return id, username
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UserPermissionInfo enthält die Berechtigungsinformationen eines Benutzers
|
||||||
|
type UserPermissionInfo struct {
|
||||||
|
UserID string
|
||||||
|
Groups []PermissionGroupInfo
|
||||||
|
HasFullAccess bool // true wenn der Benutzer mindestens eine FULL_ACCESS Gruppe hat
|
||||||
|
}
|
||||||
|
|
||||||
|
// PermissionGroupInfo enthält Informationen über eine Berechtigungsgruppe
|
||||||
|
type PermissionGroupInfo struct {
|
||||||
|
GroupID string
|
||||||
|
Permission PermissionLevel
|
||||||
|
SpaceIDs []string // Leer bedeutet Zugriff auf alle Spaces
|
||||||
|
}
|
||||||
|
|
||||||
|
// getUserPermissions ruft die Berechtigungen eines Benutzers ab
|
||||||
|
func getUserPermissions(userID string) (*UserPermissionInfo, error) {
|
||||||
|
if userID == "" {
|
||||||
|
return nil, fmt.Errorf("userID ist leer")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// Hole alle Gruppen des Benutzers mit ihren Berechtigungen
|
||||||
|
query := `
|
||||||
|
SELECT pg.id, pg.permission
|
||||||
|
FROM permission_groups pg
|
||||||
|
INNER JOIN user_groups ug ON pg.id = ug.group_id
|
||||||
|
WHERE ug.user_id = ?
|
||||||
|
`
|
||||||
|
rows, err := db.QueryContext(ctx, query, userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("fehler beim Abrufen der Benutzergruppen: %w", err)
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
info := &UserPermissionInfo{
|
||||||
|
UserID: userID,
|
||||||
|
Groups: []PermissionGroupInfo{},
|
||||||
|
}
|
||||||
|
|
||||||
|
var groupIDs []string
|
||||||
|
for rows.Next() {
|
||||||
|
var groupID string
|
||||||
|
var permission string
|
||||||
|
if err := rows.Scan(&groupID, &permission); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
groupIDs = append(groupIDs, groupID)
|
||||||
|
|
||||||
|
groupInfo := PermissionGroupInfo{
|
||||||
|
GroupID: groupID,
|
||||||
|
Permission: PermissionLevel(permission),
|
||||||
|
SpaceIDs: []string{},
|
||||||
|
}
|
||||||
|
|
||||||
|
if PermissionLevel(permission) == PermissionFullAccess {
|
||||||
|
info.HasFullAccess = true
|
||||||
|
}
|
||||||
|
|
||||||
|
info.Groups = append(info.Groups, groupInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hole Space-Zuweisungen für alle Gruppen
|
||||||
|
if len(groupIDs) > 0 {
|
||||||
|
placeholders := make([]string, len(groupIDs))
|
||||||
|
args := make([]interface{}, len(groupIDs))
|
||||||
|
for i, id := range groupIDs {
|
||||||
|
placeholders[i] = "?"
|
||||||
|
args[i] = id
|
||||||
|
}
|
||||||
|
|
||||||
|
spaceQuery := fmt.Sprintf(`
|
||||||
|
SELECT group_id, space_id
|
||||||
|
FROM group_spaces
|
||||||
|
WHERE group_id IN (%s)
|
||||||
|
`, strings.Join(placeholders, ","))
|
||||||
|
|
||||||
|
spaceRows, err := db.QueryContext(ctx, spaceQuery, args...)
|
||||||
|
if err == nil {
|
||||||
|
spaceMap := make(map[string][]string)
|
||||||
|
for spaceRows.Next() {
|
||||||
|
var groupID, spaceID string
|
||||||
|
if err := spaceRows.Scan(&groupID, &spaceID); err == nil {
|
||||||
|
spaceMap[groupID] = append(spaceMap[groupID], spaceID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spaceRows.Close()
|
||||||
|
|
||||||
|
// Aktualisiere SpaceIDs für jede Gruppe
|
||||||
|
for i := range info.Groups {
|
||||||
|
if spaceIDs, exists := spaceMap[info.Groups[i].GroupID]; exists {
|
||||||
|
info.Groups[i].SpaceIDs = spaceIDs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasSpaceAccess prüft, ob ein Benutzer Zugriff auf einen bestimmten Space hat
|
||||||
|
func hasSpaceAccess(userID, spaceID string) (bool, error) {
|
||||||
|
if userID == "" {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
permissions, err := getUserPermissions(userID)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wenn der Benutzer keine Gruppen hat, hat er keinen Zugriff
|
||||||
|
if len(permissions.Groups) == 0 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prüfe für jede Gruppe, ob der Benutzer Zugriff auf den Space hat
|
||||||
|
for _, group := range permissions.Groups {
|
||||||
|
// Wenn die Gruppe keine Spaces zugewiesen hat, hat der Benutzer Zugriff auf alle Spaces
|
||||||
|
if len(group.SpaceIDs) == 0 {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
// Prüfe, ob der Space in der Liste der zugewiesenen Spaces ist
|
||||||
|
for _, assignedSpaceID := range group.SpaceIDs {
|
||||||
|
if assignedSpaceID == spaceID {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasPermission prüft, ob ein Benutzer eine bestimmte Berechtigung für einen Space hat
|
||||||
|
// requiredPermission kann READ, READ_WRITE oder FULL_ACCESS sein
|
||||||
|
func hasPermission(userID, spaceID string, requiredPermission PermissionLevel) (bool, error) {
|
||||||
|
if userID == "" {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
permissions, err := getUserPermissions(userID)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wenn der Benutzer keine Gruppen hat, hat er keine Berechtigung
|
||||||
|
if len(permissions.Groups) == 0 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prüfe für jede Gruppe
|
||||||
|
for _, group := range permissions.Groups {
|
||||||
|
hasAccess := false
|
||||||
|
|
||||||
|
// Prüfe, ob der Benutzer Zugriff auf den Space hat
|
||||||
|
if len(group.SpaceIDs) == 0 {
|
||||||
|
// Keine Space-Zuweisungen = Zugriff auf alle Spaces
|
||||||
|
hasAccess = true
|
||||||
|
} else {
|
||||||
|
// Prüfe, ob der Space in der Liste ist
|
||||||
|
for _, assignedSpaceID := range group.SpaceIDs {
|
||||||
|
if assignedSpaceID == spaceID {
|
||||||
|
hasAccess = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasAccess {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prüfe die Berechtigungsstufe
|
||||||
|
switch requiredPermission {
|
||||||
|
case PermissionRead:
|
||||||
|
// READ, READ_WRITE und FULL_ACCESS haben alle READ-Berechtigung
|
||||||
|
return true, nil
|
||||||
|
case PermissionReadWrite:
|
||||||
|
// READ_WRITE und FULL_ACCESS haben READ_WRITE-Berechtigung
|
||||||
|
if group.Permission == PermissionReadWrite || group.Permission == PermissionFullAccess {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
case PermissionFullAccess:
|
||||||
|
// Nur FULL_ACCESS hat FULL_ACCESS-Berechtigung
|
||||||
|
if group.Permission == PermissionFullAccess {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getAccessibleSpaceIDs gibt alle Space-IDs zurück, auf die der Benutzer Zugriff hat
|
||||||
|
func getAccessibleSpaceIDs(userID string) ([]string, error) {
|
||||||
|
if userID == "" {
|
||||||
|
return []string{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
permissions, err := getUserPermissions(userID)
|
||||||
|
if err != nil {
|
||||||
|
return []string{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wenn der Benutzer keine Gruppen hat, hat er keinen Zugriff
|
||||||
|
if len(permissions.Groups) == 0 {
|
||||||
|
return []string{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sammle alle zugewiesenen Spaces
|
||||||
|
spaceIDMap := make(map[string]bool)
|
||||||
|
hasUnrestrictedAccess := false
|
||||||
|
|
||||||
|
for _, group := range permissions.Groups {
|
||||||
|
// Wenn eine Gruppe keine Spaces zugewiesen hat, hat der Benutzer Zugriff auf alle Spaces
|
||||||
|
if len(group.SpaceIDs) == 0 {
|
||||||
|
hasUnrestrictedAccess = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// Sammle alle zugewiesenen Spaces
|
||||||
|
for _, spaceID := range group.SpaceIDs {
|
||||||
|
spaceIDMap[spaceID] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wenn der Benutzer Zugriff auf alle Spaces hat, hole alle Spaces aus der DB
|
||||||
|
if hasUnrestrictedAccess {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
rows, err := db.QueryContext(ctx, "SELECT id FROM spaces")
|
||||||
|
if err != nil {
|
||||||
|
return []string{}, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var spaceIDs []string
|
||||||
|
for rows.Next() {
|
||||||
|
var spaceID string
|
||||||
|
if err := rows.Scan(&spaceID); err == nil {
|
||||||
|
spaceIDs = append(spaceIDs, spaceID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return spaceIDs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Konvertiere Map zu Slice
|
||||||
|
spaceIDs := make([]string, 0, len(spaceIDMap))
|
||||||
|
for spaceID := range spaceIDMap {
|
||||||
|
spaceIDs = append(spaceIDs, spaceID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return spaceIDs, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Helper-Funktion zum Extrahieren von IP-Adresse und User-Agent aus Request
|
// Helper-Funktion zum Extrahieren von IP-Adresse und User-Agent aus Request
|
||||||
func getRequestInfo(r *http.Request) (ipAddress, userAgent string) {
|
func getRequestInfo(r *http.Request) (ipAddress, userAgent string) {
|
||||||
// Hole IP-Adresse
|
// Hole IP-Adresse
|
||||||
@@ -4159,18 +4767,18 @@ func main() {
|
|||||||
|
|
||||||
// Protected Routes (Basic Auth erforderlich)
|
// Protected Routes (Basic Auth erforderlich)
|
||||||
api.HandleFunc("/stats", basicAuthMiddleware(getStatsHandler)).Methods("GET", "OPTIONS")
|
api.HandleFunc("/stats", basicAuthMiddleware(getStatsHandler)).Methods("GET", "OPTIONS")
|
||||||
api.HandleFunc("/spaces", getSpacesHandler).Methods("GET", "OPTIONS")
|
api.HandleFunc("/spaces", basicAuthMiddleware(getSpacesHandler)).Methods("GET", "OPTIONS")
|
||||||
api.HandleFunc("/spaces", createSpaceHandler).Methods("POST", "OPTIONS")
|
api.HandleFunc("/spaces", basicAuthMiddleware(createSpaceHandler)).Methods("POST", "OPTIONS")
|
||||||
api.HandleFunc("/spaces/{id}", deleteSpaceHandler).Methods("DELETE", "OPTIONS")
|
api.HandleFunc("/spaces/{id}", basicAuthMiddleware(deleteSpaceHandler)).Methods("DELETE", "OPTIONS")
|
||||||
api.HandleFunc("/spaces/{id}/fqdns/count", getSpaceFqdnCountHandler).Methods("GET", "OPTIONS")
|
api.HandleFunc("/spaces/{id}/fqdns/count", basicAuthMiddleware(getSpaceFqdnCountHandler)).Methods("GET", "OPTIONS")
|
||||||
api.HandleFunc("/spaces/{id}/fqdns", getFqdnsHandler).Methods("GET", "OPTIONS")
|
api.HandleFunc("/spaces/{id}/fqdns", basicAuthMiddleware(getFqdnsHandler)).Methods("GET", "OPTIONS")
|
||||||
api.HandleFunc("/spaces/{id}/fqdns", createFqdnHandler).Methods("POST", "OPTIONS")
|
api.HandleFunc("/spaces/{id}/fqdns", basicAuthMiddleware(createFqdnHandler)).Methods("POST", "OPTIONS")
|
||||||
api.HandleFunc("/spaces/{id}/fqdns", deleteAllFqdnsHandler).Methods("DELETE", "OPTIONS")
|
api.HandleFunc("/spaces/{id}/fqdns", basicAuthMiddleware(deleteAllFqdnsHandler)).Methods("DELETE", "OPTIONS")
|
||||||
api.HandleFunc("/spaces/{id}/fqdns/{fqdnId}", deleteFqdnHandler).Methods("DELETE", "OPTIONS")
|
api.HandleFunc("/spaces/{id}/fqdns/{fqdnId}", basicAuthMiddleware(deleteFqdnHandler)).Methods("DELETE", "OPTIONS")
|
||||||
api.HandleFunc("/fqdns", deleteAllFqdnsGlobalHandler).Methods("DELETE", "OPTIONS")
|
api.HandleFunc("/fqdns", basicAuthMiddleware(deleteAllFqdnsGlobalHandler)).Methods("DELETE", "OPTIONS")
|
||||||
api.HandleFunc("/spaces/{spaceId}/fqdns/{fqdnId}/csr", uploadCSRHandler).Methods("POST", "OPTIONS")
|
api.HandleFunc("/spaces/{spaceId}/fqdns/{fqdnId}/csr", basicAuthMiddleware(uploadCSRHandler)).Methods("POST", "OPTIONS")
|
||||||
api.HandleFunc("/spaces/{spaceId}/fqdns/{fqdnId}/csr", getCSRByFQDNHandler).Methods("GET", "OPTIONS")
|
api.HandleFunc("/spaces/{spaceId}/fqdns/{fqdnId}/csr", basicAuthMiddleware(getCSRByFQDNHandler)).Methods("GET", "OPTIONS")
|
||||||
api.HandleFunc("/csrs", deleteAllCSRsHandler).Methods("DELETE", "OPTIONS")
|
api.HandleFunc("/csrs", basicAuthMiddleware(deleteAllCSRsHandler)).Methods("DELETE", "OPTIONS")
|
||||||
|
|
||||||
// User Routes
|
// User Routes
|
||||||
api.HandleFunc("/users", getUsersHandler).Methods("GET", "OPTIONS")
|
api.HandleFunc("/users", getUsersHandler).Methods("GET", "OPTIONS")
|
||||||
@@ -4180,6 +4788,7 @@ func main() {
|
|||||||
api.HandleFunc("/users/{id}", deleteUserHandler).Methods("DELETE", "OPTIONS")
|
api.HandleFunc("/users/{id}", deleteUserHandler).Methods("DELETE", "OPTIONS")
|
||||||
api.HandleFunc("/users/{id}/avatar", basicAuthMiddleware(getAvatarHandler)).Methods("GET", "OPTIONS")
|
api.HandleFunc("/users/{id}/avatar", basicAuthMiddleware(getAvatarHandler)).Methods("GET", "OPTIONS")
|
||||||
api.HandleFunc("/users/{id}/avatar", basicAuthMiddleware(uploadAvatarHandler)).Methods("POST", "OPTIONS")
|
api.HandleFunc("/users/{id}/avatar", basicAuthMiddleware(uploadAvatarHandler)).Methods("POST", "OPTIONS")
|
||||||
|
api.HandleFunc("/user/permissions", basicAuthMiddleware(getUserPermissionsHandler)).Methods("GET", "OPTIONS")
|
||||||
|
|
||||||
// Permission Groups Routes (Protected)
|
// Permission Groups Routes (Protected)
|
||||||
api.HandleFunc("/permission-groups", basicAuthMiddleware(getPermissionGroupsHandler)).Methods("GET", "OPTIONS")
|
api.HandleFunc("/permission-groups", basicAuthMiddleware(getPermissionGroupsHandler)).Methods("GET", "OPTIONS")
|
||||||
@@ -4477,6 +5086,25 @@ func signCSRHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
spaceID := vars["spaceId"]
|
spaceID := vars["spaceId"]
|
||||||
fqdnID := vars["fqdnId"]
|
fqdnID := vars["fqdnId"]
|
||||||
|
|
||||||
|
// Prüfe Berechtigung: READ_WRITE oder FULL_ACCESS erforderlich zum Signieren von CSRs
|
||||||
|
userID, username := getUserFromRequest(r)
|
||||||
|
if userID == "" {
|
||||||
|
http.Error(w, "Nicht authentifiziert", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hasPermission, err := hasPermission(userID, spaceID, PermissionReadWrite)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Fehler beim Prüfen der Berechtigung", http.StatusInternalServerError)
|
||||||
|
log.Printf("Fehler beim Prüfen der Berechtigung: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasPermission {
|
||||||
|
http.Error(w, "Keine Berechtigung zum Signieren von CSRs. Lesen/Schreiben oder Vollzugriff erforderlich.", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var req struct {
|
var req struct {
|
||||||
ProviderID string `json:"providerId"`
|
ProviderID string `json:"providerId"`
|
||||||
CSRID string `json:"csrId,omitempty"` // Optional: spezifischer CSR, sonst neuester
|
CSRID string `json:"csrId,omitempty"` // Optional: spezifischer CSR, sonst neuester
|
||||||
@@ -4495,7 +5123,7 @@ func signCSRHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
// Hole neuesten CSR für den FQDN
|
// Hole neuesten CSR für den FQDN
|
||||||
var csrPEM string
|
var csrPEM string
|
||||||
var csrID string
|
var csrID string
|
||||||
err := db.QueryRow(`
|
err = db.QueryRow(`
|
||||||
SELECT id, csr_pem
|
SELECT id, csr_pem
|
||||||
FROM csrs
|
FROM csrs
|
||||||
WHERE fqdn_id = ? AND space_id = ?
|
WHERE fqdn_id = ? AND space_id = ?
|
||||||
@@ -4579,7 +5207,6 @@ func signCSRHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Audit-Log: CSR signiert
|
// Audit-Log: CSR signiert
|
||||||
userID, username := getUserFromRequest(r)
|
|
||||||
ipAddress, userAgent := getRequestInfo(r)
|
ipAddress, userAgent := getRequestInfo(r)
|
||||||
auditService.Track(r.Context(), "SIGN", "csr", csrID, userID, username, map[string]interface{}{
|
auditService.Track(r.Context(), "SIGN", "csr", csrID, userID, username, map[string]interface{}{
|
||||||
"providerId": req.ProviderID,
|
"providerId": req.ProviderID,
|
||||||
@@ -4606,6 +5233,25 @@ func getCertificatesHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
spaceID := vars["spaceId"]
|
spaceID := vars["spaceId"]
|
||||||
fqdnID := vars["fqdnId"]
|
fqdnID := vars["fqdnId"]
|
||||||
|
|
||||||
|
// Prüfe Berechtigung: Benutzer muss Zugriff auf den Space haben (READ, READ_WRITE oder FULL_ACCESS)
|
||||||
|
userID, _ := getUserFromRequest(r)
|
||||||
|
if userID == "" {
|
||||||
|
http.Error(w, "Nicht authentifiziert", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hasAccess, err := hasSpaceAccess(userID, spaceID)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Fehler beim Prüfen der Berechtigung", http.StatusInternalServerError)
|
||||||
|
log.Printf("Fehler beim Prüfen der Berechtigung: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasAccess {
|
||||||
|
http.Error(w, "Keine Berechtigung für diesen Space", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Hole alle Zertifikate für diesen FQDN
|
// Hole alle Zertifikate für diesen FQDN
|
||||||
rows, err := db.Query(`
|
rows, err := db.Query(`
|
||||||
SELECT id, csr_id, certificate_id, provider_id, certificate_pem, status, created_at
|
SELECT id, csr_id, certificate_id, provider_id, certificate_pem, status, created_at
|
||||||
@@ -4661,9 +5307,28 @@ func refreshCertificateHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
fqdnID := vars["fqdnId"]
|
fqdnID := vars["fqdnId"]
|
||||||
certID := vars["certId"]
|
certID := vars["certId"]
|
||||||
|
|
||||||
|
// Prüfe Berechtigung: Benutzer muss Zugriff auf den Space haben (READ, READ_WRITE oder FULL_ACCESS)
|
||||||
|
userID, _ := getUserFromRequest(r)
|
||||||
|
if userID == "" {
|
||||||
|
http.Error(w, "Nicht authentifiziert", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hasAccess, err := hasSpaceAccess(userID, spaceID)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Fehler beim Prüfen der Berechtigung", http.StatusInternalServerError)
|
||||||
|
log.Printf("Fehler beim Prüfen der Berechtigung: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasAccess {
|
||||||
|
http.Error(w, "Keine Berechtigung für diesen Space", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Hole Zertifikat aus DB
|
// Hole Zertifikat aus DB
|
||||||
var certificateID, providerID string
|
var certificateID, providerID string
|
||||||
err := db.QueryRow(`
|
err = db.QueryRow(`
|
||||||
SELECT certificate_id, provider_id
|
SELECT certificate_id, provider_id
|
||||||
FROM certificates
|
FROM certificates
|
||||||
WHERE id = ? AND fqdn_id = ? AND space_id = ?
|
WHERE id = ? AND fqdn_id = ? AND space_id = ?
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -163,6 +163,7 @@ const AuditLogs = () => {
|
|||||||
csr: 'CSR',
|
csr: 'CSR',
|
||||||
provider: 'Provider',
|
provider: 'Provider',
|
||||||
certificate: 'Zertifikat',
|
certificate: 'Zertifikat',
|
||||||
|
permission_group: 'Berechtigungsgruppen',
|
||||||
}
|
}
|
||||||
|
|
||||||
const toggleLogExpansion = (logId) => {
|
const toggleLogExpansion = (logId) => {
|
||||||
@@ -239,6 +240,7 @@ const AuditLogs = () => {
|
|||||||
<option value="csr">CSR</option>
|
<option value="csr">CSR</option>
|
||||||
<option value="provider">Provider</option>
|
<option value="provider">Provider</option>
|
||||||
<option value="certificate">Zertifikat</option>
|
<option value="certificate">Zertifikat</option>
|
||||||
|
<option value="permission_group">Berechtigungsgruppen</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { useParams, useNavigate } from 'react-router-dom'
|
import { useParams, useNavigate } from 'react-router-dom'
|
||||||
import { useAuth } from '../contexts/AuthContext'
|
import { useAuth } from '../contexts/AuthContext'
|
||||||
|
import { usePermissions } from '../hooks/usePermissions'
|
||||||
|
|
||||||
const SpaceDetail = () => {
|
const SpaceDetail = () => {
|
||||||
const { id } = useParams()
|
const { id } = useParams()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const { authFetch } = useAuth()
|
const { authFetch } = useAuth()
|
||||||
|
const { canCreateFqdn, canDeleteFqdn, canSignCSR, canUploadCSR, refreshPermissions } = usePermissions()
|
||||||
const [space, setSpace] = useState(null)
|
const [space, setSpace] = useState(null)
|
||||||
const [fqdns, setFqdns] = useState([])
|
const [fqdns, setFqdns] = useState([])
|
||||||
const [showForm, setShowForm] = useState(false)
|
const [showForm, setShowForm] = useState(false)
|
||||||
@@ -123,6 +125,8 @@ const SpaceDetail = () => {
|
|||||||
setFqdns([...fqdns, newFqdn])
|
setFqdns([...fqdns, newFqdn])
|
||||||
setFormData({ fqdn: '', description: '' })
|
setFormData({ fqdn: '', description: '' })
|
||||||
setShowForm(false)
|
setShowForm(false)
|
||||||
|
// Aktualisiere Berechtigungen nach dem Erstellen eines FQDNs
|
||||||
|
refreshPermissions()
|
||||||
} else {
|
} else {
|
||||||
let errorMessage = 'Fehler beim Erstellen des FQDN'
|
let errorMessage = 'Fehler beim Erstellen des FQDN'
|
||||||
try {
|
try {
|
||||||
@@ -176,6 +180,8 @@ const SpaceDetail = () => {
|
|||||||
setShowDeleteModal(false)
|
setShowDeleteModal(false)
|
||||||
setFqdnToDelete(null)
|
setFqdnToDelete(null)
|
||||||
setConfirmChecked(false)
|
setConfirmChecked(false)
|
||||||
|
// Aktualisiere Berechtigungen nach dem Löschen eines FQDNs
|
||||||
|
refreshPermissions()
|
||||||
} else {
|
} else {
|
||||||
const errorData = await response.json().catch(() => ({ error: 'Fehler beim Löschen' }))
|
const errorData = await response.json().catch(() => ({ error: 'Fehler beim Löschen' }))
|
||||||
alert(errorData.error || 'Fehler beim Löschen des FQDN')
|
alert(errorData.error || 'Fehler beim Löschen des FQDN')
|
||||||
@@ -586,7 +592,13 @@ const SpaceDetail = () => {
|
|||||||
</h3>
|
</h3>
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowForm(!showForm)}
|
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'}
|
{showForm ? 'Abbrechen' : '+ Neuer FQDN'}
|
||||||
</button>
|
</button>
|
||||||
@@ -630,11 +642,18 @@ const SpaceDetail = () => {
|
|||||||
<button
|
<button
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation()
|
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"
|
disabled={!canSignCSR(id)}
|
||||||
title="CSR signieren lassen"
|
className={`p-2 rounded-lg transition-colors ${
|
||||||
aria-label="CSR signieren lassen"
|
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
|
<svg
|
||||||
className="w-5 h-5"
|
className="w-5 h-5"
|
||||||
@@ -692,16 +711,23 @@ const SpaceDetail = () => {
|
|||||||
}
|
}
|
||||||
e.target.value = ''
|
e.target.value = ''
|
||||||
}}
|
}}
|
||||||
disabled={uploadingCSR}
|
disabled={uploadingCSR || !canUploadCSR(id)}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
className="p-2 text-blue-400 hover:text-blue-300 hover:bg-blue-500/20 rounded-lg transition-colors"
|
disabled={!canUploadCSR(id)}
|
||||||
title="CSR hochladen"
|
className={`p-2 rounded-lg transition-colors ${
|
||||||
aria-label="CSR hochladen"
|
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) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
e.currentTarget.parentElement.querySelector('input[type="file"]')?.click()
|
if (canUploadCSR(id)) {
|
||||||
|
e.currentTarget.parentElement.querySelector('input[type="file"]')?.click()
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
@@ -806,11 +832,18 @@ const SpaceDetail = () => {
|
|||||||
<button
|
<button
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation()
|
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"
|
disabled={!canDeleteFqdn(id)}
|
||||||
title="FQDN löschen"
|
className={`p-2 rounded-lg transition-colors ${
|
||||||
aria-label="FQDN löschen"
|
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
|
<svg
|
||||||
className="w-5 h-5"
|
className="w-5 h-5"
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { useAuth } from '../contexts/AuthContext'
|
import { useAuth } from '../contexts/AuthContext'
|
||||||
|
import { usePermissions } from '../hooks/usePermissions'
|
||||||
|
|
||||||
const Spaces = () => {
|
const Spaces = () => {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const { authFetch } = useAuth()
|
const { authFetch } = useAuth()
|
||||||
|
const { canCreateSpace, canDeleteSpace, refreshPermissions } = usePermissions()
|
||||||
const [spaces, setSpaces] = useState([])
|
const [spaces, setSpaces] = useState([])
|
||||||
const [showForm, setShowForm] = useState(false)
|
const [showForm, setShowForm] = useState(false)
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
@@ -72,6 +74,8 @@ const Spaces = () => {
|
|||||||
setSpaces([...spaces, newSpace])
|
setSpaces([...spaces, newSpace])
|
||||||
setFormData({ name: '', description: '' })
|
setFormData({ name: '', description: '' })
|
||||||
setShowForm(false)
|
setShowForm(false)
|
||||||
|
// Aktualisiere Berechtigungen nach dem Erstellen eines Spaces
|
||||||
|
refreshPermissions()
|
||||||
} else {
|
} else {
|
||||||
const errorData = await response.json()
|
const errorData = await response.json()
|
||||||
setError(errorData.error || 'Fehler beim Erstellen des Space')
|
setError(errorData.error || 'Fehler beim Erstellen des Space')
|
||||||
@@ -140,6 +144,8 @@ const Spaces = () => {
|
|||||||
setConfirmChecked(false)
|
setConfirmChecked(false)
|
||||||
setDeleteFqdnsChecked(false)
|
setDeleteFqdnsChecked(false)
|
||||||
setFqdnCount(0)
|
setFqdnCount(0)
|
||||||
|
// Aktualisiere Berechtigungen nach dem Löschen eines Spaces
|
||||||
|
refreshPermissions()
|
||||||
} else {
|
} else {
|
||||||
const errorText = await response.text()
|
const errorText = await response.text()
|
||||||
let errorMessage = 'Fehler beim Löschen des Space'
|
let errorMessage = 'Fehler beim Löschen des Space'
|
||||||
@@ -188,7 +194,13 @@ const Spaces = () => {
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowForm(!showForm)}
|
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'}
|
{showForm ? 'Abbrechen' : '+ Neuer Space'}
|
||||||
</button>
|
</button>
|
||||||
@@ -340,11 +352,18 @@ const Spaces = () => {
|
|||||||
<button
|
<button
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation()
|
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"
|
disabled={!canDeleteSpace(space.id)}
|
||||||
title="Space löschen"
|
className={`p-2 rounded-lg transition-colors ${
|
||||||
aria-label="Space löschen"
|
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
|
<svg
|
||||||
className="w-5 h-5"
|
className="w-5 h-5"
|
||||||
|
|||||||
Reference in New Issue
Block a user