fixed permission implementation for ressources

This commit is contained in:
2025-11-21 00:58:34 +01:00
parent 0d17fda341
commit d23bfa0376
6 changed files with 857 additions and 54 deletions

View File

@@ -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.