feature/permissionsAndRoles #2
735
backend/main.go
735
backend/main.go
@@ -823,16 +823,35 @@ func getSpacesHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Verwende Prepared Statement für bessere Performance und Sicherheit
|
||||
stmt, err := db.Prepare("SELECT id, name, description, created_at FROM spaces ORDER BY created_at DESC")
|
||||
// Hole Benutzer-ID
|
||||
userID, _ := getUserFromRequest(r)
|
||||
|
||||
// Hole alle Spaces, auf die der Benutzer Zugriff hat
|
||||
accessibleSpaceIDs, err := getAccessibleSpaceIDs(userID)
|
||||
if err != nil {
|
||||
http.Error(w, "Fehler beim Vorbereiten der Abfrage", http.StatusInternalServerError)
|
||||
log.Printf("Fehler beim Vorbereiten der Abfrage: %v", err)
|
||||
http.Error(w, "Fehler beim Prüfen der Berechtigungen", http.StatusInternalServerError)
|
||||
log.Printf("Fehler beim Prüfen der Berechtigungen: %v", err)
|
||||
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 {
|
||||
http.Error(w, "Fehler beim Abrufen der Spaces", http.StatusInternalServerError)
|
||||
log.Printf("Fehler beim Abrufen der Spaces: %v", err)
|
||||
@@ -888,6 +907,33 @@ func createSpaceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
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
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||
@@ -904,7 +950,7 @@ func createSpaceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
createdAt := time.Now()
|
||||
|
||||
// Speichere in Datenbank
|
||||
_, err := db.Exec(
|
||||
_, err = db.Exec(
|
||||
"INSERT INTO spaces (id, name, description, created_at) VALUES (?, ?, ?, ?)",
|
||||
id, req.Name, req.Description, createdAt,
|
||||
)
|
||||
@@ -926,7 +972,6 @@ func createSpaceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Audit-Log: Space erstellt
|
||||
if auditService != nil {
|
||||
userID, username := getUserFromRequest(r)
|
||||
ipAddress, userAgent := getRequestInfo(r)
|
||||
auditService.Track(r.Context(), "CREATE", "space", id, userID, username, map[string]interface{}{
|
||||
"name": req.Name,
|
||||
@@ -955,9 +1000,28 @@ func deleteSpaceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
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
|
||||
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 {
|
||||
http.Error(w, "Fehler beim Prüfen des Space", http.StatusInternalServerError)
|
||||
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"})
|
||||
|
||||
// Audit-Log: Space gelöscht
|
||||
userID, username := getUserFromRequest(r)
|
||||
ipAddress, userAgent := getRequestInfo(r)
|
||||
details := map[string]interface{}{
|
||||
"message": fmt.Sprintf("Space gelöscht: %s", id),
|
||||
@@ -1071,8 +1134,27 @@ func getSpaceFqdnCountHandler(w http.ResponseWriter, r *http.Request) {
|
||||
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
|
||||
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 {
|
||||
http.Error(w, "Fehler beim Abrufen der FQDN-Anzahl", http.StatusInternalServerError)
|
||||
log.Printf("Fehler beim Abrufen der FQDN-Anzahl: %v", err)
|
||||
@@ -1101,9 +1183,28 @@ func getFqdnsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
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
|
||||
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 {
|
||||
http.Error(w, "Fehler beim Prüfen des Space", http.StatusInternalServerError)
|
||||
log.Printf("Fehler beim Prüfen des Space: %v", err)
|
||||
@@ -1175,9 +1276,28 @@ func createFqdnHandler(w http.ResponseWriter, r *http.Request) {
|
||||
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
|
||||
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 {
|
||||
http.Error(w, "Fehler beim Prüfen des Space", http.StatusInternalServerError)
|
||||
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)
|
||||
|
||||
// Audit-Log: FQDN erstellt
|
||||
userID, username := getUserFromRequest(r)
|
||||
ipAddress, userAgent := getRequestInfo(r)
|
||||
auditService.Track(r.Context(), "CREATE", "fqdn", id, userID, username, map[string]interface{}{
|
||||
"fqdn": req.FQDN,
|
||||
@@ -1271,9 +1390,28 @@ func deleteFqdnHandler(w http.ResponseWriter, r *http.Request) {
|
||||
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
|
||||
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 {
|
||||
http.Error(w, "Fehler beim Prüfen des FQDN", http.StatusInternalServerError)
|
||||
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"})
|
||||
|
||||
// Audit-Log: FQDN gelöscht
|
||||
userID, username := getUserFromRequest(r)
|
||||
ipAddress, userAgent := getRequestInfo(r)
|
||||
auditService.Track(r.Context(), "DELETE", "fqdn", fqdnID, userID, username, map[string]interface{}{
|
||||
"spaceId": spaceID,
|
||||
@@ -1364,9 +1501,28 @@ func deleteAllFqdnsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
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
|
||||
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 {
|
||||
http.Error(w, "Fehler beim Prüfen des Space", http.StatusInternalServerError)
|
||||
log.Printf("Fehler beim Prüfen des Space: %v", err)
|
||||
@@ -1438,6 +1594,32 @@ func deleteAllFqdnsGlobalHandler(w http.ResponseWriter, r *http.Request) {
|
||||
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)
|
||||
confirm := r.URL.Query().Get("confirm")
|
||||
if confirm != "true" {
|
||||
@@ -1447,7 +1629,7 @@ func deleteAllFqdnsGlobalHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Zähle zuerst die Anzahl der FQDNs
|
||||
var totalCount int
|
||||
err := db.QueryRow("SELECT COUNT(*) FROM fqdns").Scan(&totalCount)
|
||||
err = db.QueryRow("SELECT COUNT(*) FROM fqdns").Scan(&totalCount)
|
||||
if err != nil {
|
||||
http.Error(w, "Fehler beim Zählen der FQDNs", http.StatusInternalServerError)
|
||||
log.Printf("Fehler beim Zählen der FQDNs: %v", err)
|
||||
@@ -1523,6 +1705,32 @@ func deleteAllCSRsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
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)
|
||||
confirm := r.URL.Query().Get("confirm")
|
||||
if confirm != "true" {
|
||||
@@ -1532,7 +1740,7 @@ func deleteAllCSRsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Zähle zuerst die Anzahl der CSRs
|
||||
var totalCount int
|
||||
err := db.QueryRow("SELECT COUNT(*) FROM csrs").Scan(&totalCount)
|
||||
err = db.QueryRow("SELECT COUNT(*) FROM csrs").Scan(&totalCount)
|
||||
if err != nil {
|
||||
http.Error(w, "Fehler beim Zählen der CSRs", http.StatusInternalServerError)
|
||||
log.Printf("Fehler beim Zählen der CSRs: %v", err)
|
||||
@@ -1615,6 +1823,25 @@ func uploadCSRHandler(w http.ResponseWriter, r *http.Request) {
|
||||
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
|
||||
var spaceExists bool
|
||||
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)
|
||||
|
||||
// Audit-Log: CSR hochgeladen
|
||||
userID, username := getUserFromRequest(r)
|
||||
ipAddress, userAgent := getRequestInfo(r)
|
||||
auditService.Track(r.Context(), "UPLOAD", "csr", csrID, userID, username, map[string]interface{}{
|
||||
"fqdnId": fqdnID,
|
||||
@@ -1790,6 +2016,25 @@ func getCSRByFQDNHandler(w http.ResponseWriter, r *http.Request) {
|
||||
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
|
||||
latestOnly := r.URL.Query().Get("latest") == "true"
|
||||
|
||||
@@ -2502,6 +2747,113 @@ components:
|
||||
|
||||
// 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) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
@@ -3612,6 +3964,262 @@ func getUserFromRequest(r *http.Request) (userID, username string) {
|
||||
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
|
||||
func getRequestInfo(r *http.Request) (ipAddress, userAgent string) {
|
||||
// Hole IP-Adresse
|
||||
@@ -4159,18 +4767,18 @@ func main() {
|
||||
|
||||
// Protected Routes (Basic Auth erforderlich)
|
||||
api.HandleFunc("/stats", basicAuthMiddleware(getStatsHandler)).Methods("GET", "OPTIONS")
|
||||
api.HandleFunc("/spaces", getSpacesHandler).Methods("GET", "OPTIONS")
|
||||
api.HandleFunc("/spaces", createSpaceHandler).Methods("POST", "OPTIONS")
|
||||
api.HandleFunc("/spaces/{id}", deleteSpaceHandler).Methods("DELETE", "OPTIONS")
|
||||
api.HandleFunc("/spaces/{id}/fqdns/count", getSpaceFqdnCountHandler).Methods("GET", "OPTIONS")
|
||||
api.HandleFunc("/spaces/{id}/fqdns", getFqdnsHandler).Methods("GET", "OPTIONS")
|
||||
api.HandleFunc("/spaces/{id}/fqdns", createFqdnHandler).Methods("POST", "OPTIONS")
|
||||
api.HandleFunc("/spaces/{id}/fqdns", deleteAllFqdnsHandler).Methods("DELETE", "OPTIONS")
|
||||
api.HandleFunc("/spaces/{id}/fqdns/{fqdnId}", deleteFqdnHandler).Methods("DELETE", "OPTIONS")
|
||||
api.HandleFunc("/fqdns", deleteAllFqdnsGlobalHandler).Methods("DELETE", "OPTIONS")
|
||||
api.HandleFunc("/spaces/{spaceId}/fqdns/{fqdnId}/csr", uploadCSRHandler).Methods("POST", "OPTIONS")
|
||||
api.HandleFunc("/spaces/{spaceId}/fqdns/{fqdnId}/csr", getCSRByFQDNHandler).Methods("GET", "OPTIONS")
|
||||
api.HandleFunc("/csrs", deleteAllCSRsHandler).Methods("DELETE", "OPTIONS")
|
||||
api.HandleFunc("/spaces", basicAuthMiddleware(getSpacesHandler)).Methods("GET", "OPTIONS")
|
||||
api.HandleFunc("/spaces", basicAuthMiddleware(createSpaceHandler)).Methods("POST", "OPTIONS")
|
||||
api.HandleFunc("/spaces/{id}", basicAuthMiddleware(deleteSpaceHandler)).Methods("DELETE", "OPTIONS")
|
||||
api.HandleFunc("/spaces/{id}/fqdns/count", basicAuthMiddleware(getSpaceFqdnCountHandler)).Methods("GET", "OPTIONS")
|
||||
api.HandleFunc("/spaces/{id}/fqdns", basicAuthMiddleware(getFqdnsHandler)).Methods("GET", "OPTIONS")
|
||||
api.HandleFunc("/spaces/{id}/fqdns", basicAuthMiddleware(createFqdnHandler)).Methods("POST", "OPTIONS")
|
||||
api.HandleFunc("/spaces/{id}/fqdns", basicAuthMiddleware(deleteAllFqdnsHandler)).Methods("DELETE", "OPTIONS")
|
||||
api.HandleFunc("/spaces/{id}/fqdns/{fqdnId}", basicAuthMiddleware(deleteFqdnHandler)).Methods("DELETE", "OPTIONS")
|
||||
api.HandleFunc("/fqdns", basicAuthMiddleware(deleteAllFqdnsGlobalHandler)).Methods("DELETE", "OPTIONS")
|
||||
api.HandleFunc("/spaces/{spaceId}/fqdns/{fqdnId}/csr", basicAuthMiddleware(uploadCSRHandler)).Methods("POST", "OPTIONS")
|
||||
api.HandleFunc("/spaces/{spaceId}/fqdns/{fqdnId}/csr", basicAuthMiddleware(getCSRByFQDNHandler)).Methods("GET", "OPTIONS")
|
||||
api.HandleFunc("/csrs", basicAuthMiddleware(deleteAllCSRsHandler)).Methods("DELETE", "OPTIONS")
|
||||
|
||||
// User Routes
|
||||
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}/avatar", basicAuthMiddleware(getAvatarHandler)).Methods("GET", "OPTIONS")
|
||||
api.HandleFunc("/users/{id}/avatar", basicAuthMiddleware(uploadAvatarHandler)).Methods("POST", "OPTIONS")
|
||||
api.HandleFunc("/user/permissions", basicAuthMiddleware(getUserPermissionsHandler)).Methods("GET", "OPTIONS")
|
||||
|
||||
// Permission Groups Routes (Protected)
|
||||
api.HandleFunc("/permission-groups", basicAuthMiddleware(getPermissionGroupsHandler)).Methods("GET", "OPTIONS")
|
||||
@@ -4477,6 +5086,25 @@ func signCSRHandler(w http.ResponseWriter, r *http.Request) {
|
||||
spaceID := vars["spaceId"]
|
||||
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 {
|
||||
ProviderID string `json:"providerId"`
|
||||
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
|
||||
var csrPEM string
|
||||
var csrID string
|
||||
err := db.QueryRow(`
|
||||
err = db.QueryRow(`
|
||||
SELECT id, csr_pem
|
||||
FROM csrs
|
||||
WHERE fqdn_id = ? AND space_id = ?
|
||||
@@ -4579,7 +5207,6 @@ func signCSRHandler(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
|
||||
// Audit-Log: CSR signiert
|
||||
userID, username := getUserFromRequest(r)
|
||||
ipAddress, userAgent := getRequestInfo(r)
|
||||
auditService.Track(r.Context(), "SIGN", "csr", csrID, userID, username, map[string]interface{}{
|
||||
"providerId": req.ProviderID,
|
||||
@@ -4606,6 +5233,25 @@ func getCertificatesHandler(w http.ResponseWriter, r *http.Request) {
|
||||
spaceID := vars["spaceId"]
|
||||
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
|
||||
rows, err := db.Query(`
|
||||
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"]
|
||||
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
|
||||
var certificateID, providerID string
|
||||
err := db.QueryRow(`
|
||||
err = db.QueryRow(`
|
||||
SELECT certificate_id, provider_id
|
||||
FROM certificates
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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