From 9bf36599fb6cad9f5031eb9693c357fb70121457 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 1 Feb 2026 22:48:07 +0100 Subject: [PATCH] remove edit elements if user is not owner --- backend/character/handlers.go | 54 +++++++++++++++++ backend/character/image_handler.go | 5 ++ backend/character/practice_points_handler.go | 15 +++++ backend/equipment/handlers.go | 63 +++++++++++++++++++- backend/gsmaster/routes.go | 44 +++++++------- frontend/src/components/CharacterDetails.vue | 11 +++- frontend/src/components/DeleteCharView.vue | 15 ++++- frontend/src/components/EquipmentView.vue | 28 ++++++++- frontend/src/components/ExperianceView.vue | 8 ++- frontend/src/components/SkillView.vue | 12 +++- frontend/src/components/SpellView.vue | 6 +- frontend/src/components/WeaponView.vue | 10 +++- frontend/src/locales/de | 3 + frontend/src/locales/en | 3 + 14 files changed, 237 insertions(+), 40 deletions(-) diff --git a/backend/character/handlers.go b/backend/character/handlers.go index bf3b6dd..66855fb 100644 --- a/backend/character/handlers.go +++ b/backend/character/handlers.go @@ -30,6 +30,17 @@ func respondWithError(c *gin.Context, status int, message string) { c.JSON(status, gin.H{"error": message}) } +// checkCharacterOwnership verifies that the logged-in user owns the character +func checkCharacterOwnership(c *gin.Context, character *models.Char) bool { + userID := c.GetUint("userID") + if character.UserID != userID { + logger.Warn("Unauthorized access attempt: user %d tried to modify character %d owned by user %d", userID, character.ID, character.UserID) + respondWithError(c, http.StatusForbidden, "You are not authorized to modify this character") + return false + } + return true +} + func ListCharacters(c *gin.Context) { logger.Debug("ListCharacters aufgerufen") @@ -109,6 +120,11 @@ func UpdateCharacter(c *gin.Context) { return } + // Check ownership + if !checkCharacterOwnership(c, &character) { + return + } + // Store the original ID to preserve it originalID := character.ID originalGameSystem := character.GameSystem @@ -141,6 +157,12 @@ func DeleteCharacter(c *gin.Context) { respondWithError(c, http.StatusNotFound, "Character not found") return } + + // Check ownership + if !checkCharacterOwnership(c, &character) { + return + } + err = character.Delete() if err != nil { respondWithError(c, http.StatusInternalServerError, "Failed to delete character") @@ -275,6 +297,11 @@ func UpdateCharacterExperience(c *gin.Context) { return } + // Check ownership + if !checkCharacterOwnership(c, &character) { + return + } + // Parse Request var req UpdateExperienceRequest if err := c.ShouldBindJSON(&req); err != nil { @@ -365,6 +392,11 @@ func UpdateCharacterWealth(c *gin.Context) { return } + // Check ownership + if !checkCharacterOwnership(c, &character) { + return + } + // Parse Request var req UpdateWealthRequest if err := c.ShouldBindJSON(&req); err != nil { @@ -575,6 +607,11 @@ func LearnSkill(c *gin.Context) { return } + // Check ownership + if !checkCharacterOwnership(c, &character) { + return + } + // Verwende gsmaster.LernCostRequest direkt var request gsmaster.LernCostRequest if err := c.ShouldBindJSON(&request); err != nil { @@ -1060,6 +1097,11 @@ func ImproveSkill(c *gin.Context) { return } + // Check ownership + if !checkCharacterOwnership(c, char) { + return + } + // 2. Skill validieren und Level ermitteln characterClass, skillInfo, currentLevel, err := validateSkillForImprovement(char, &request) if err != nil { @@ -1233,6 +1275,18 @@ func LearnSpell(c *gin.Context) { } charID := uint(charIDInt) + // Load character to check ownership + var character models.Char + if err := character.FirstID(char_ID); err != nil { + respondWithError(c, http.StatusNotFound, "Charakter nicht gefunden") + return + } + + // Check ownership + if !checkCharacterOwnership(c, &character) { + return + } + var lernRequest gsmaster.LernCostRequest if err := c.ShouldBindJSON(&lernRequest); err != nil { respondWithError(c, http.StatusBadRequest, "Ungültige Anfrageparameter: "+err.Error()) diff --git a/backend/character/image_handler.go b/backend/character/image_handler.go index b88b6da..f79bc86 100644 --- a/backend/character/image_handler.go +++ b/backend/character/image_handler.go @@ -25,6 +25,11 @@ func UpdateCharacterImage(c *gin.Context) { return } + // Check ownership + if !checkCharacterOwnership(c, &character) { + return + } + var request ImageUpdateRequest if err := c.ShouldBindJSON(&request); err != nil { logger.Error("Invalid request data: %s", err.Error()) diff --git a/backend/character/practice_points_handler.go b/backend/character/practice_points_handler.go index 5c3d41a..714dca0 100644 --- a/backend/character/practice_points_handler.go +++ b/backend/character/practice_points_handler.go @@ -63,6 +63,11 @@ func UpdatePracticePoints(c *gin.Context) { return } + // Check ownership + if !checkCharacterOwnership(c, &character) { + return + } + // Request-Parameter abrufen var practicePoints []PracticePointResponse if err := c.ShouldBindJSON(&practicePoints); err != nil { @@ -120,6 +125,11 @@ func AddPracticePoint(c *gin.Context) { return } + // Check ownership + if !checkCharacterOwnership(c, &character) { + return + } + // Request-Parameter abrufen type AddPPRequest struct { SkillName string `json:"skill_name" binding:"required"` @@ -218,6 +228,11 @@ func UsePracticePoint(c *gin.Context) { return } + // Check ownership + if !checkCharacterOwnership(c, &character) { + return + } + // Request-Parameter abrufen type UsePPRequest struct { SkillName string `json:"skill_name" binding:"required"` diff --git a/backend/equipment/handlers.go b/backend/equipment/handlers.go index be68e2f..abe4e16 100644 --- a/backend/equipment/handlers.go +++ b/backend/equipment/handlers.go @@ -19,6 +19,21 @@ func respondWithError(c *gin.Context, status int, message string) { c.JSON(status, gin.H{"error": message}) } +// checkEquipmentOwnership verifies that the logged-in user owns the equipment's character +func checkEquipmentOwnership(c *gin.Context, characterID uint) bool { + userID := c.GetUint("userID") + var character models.Char + if err := database.DB.Select("id", "user_id").First(&character, characterID).Error; err != nil { + respondWithError(c, http.StatusNotFound, "Character not found") + return false + } + if character.UserID != userID { + respondWithError(c, http.StatusForbidden, "You are not authorized to modify this character's equipment") + return false + } + return true +} + func CreateAusruestung(c *gin.Context) { var ausruestung models.EqAusruestung if err := c.ShouldBindJSON(&ausruestung); err != nil { @@ -26,6 +41,11 @@ func CreateAusruestung(c *gin.Context) { return } + // Check ownership + if !checkEquipmentOwnership(c, ausruestung.CharacterID) { + return + } + if err := database.DB.Create(&ausruestung).Error; err != nil { respondWithError(c, http.StatusInternalServerError, "Failed to create Ausruestung") return @@ -55,6 +75,11 @@ func UpdateAusruestung(c *gin.Context) { return } + // Check ownership + if !checkEquipmentOwnership(c, ausruestung.CharacterID) { + return + } + if err := c.ShouldBindJSON(&ausruestung); err != nil { respondWithError(c, http.StatusBadRequest, err.Error()) return @@ -70,7 +95,19 @@ func UpdateAusruestung(c *gin.Context) { func DeleteAusruestung(c *gin.Context) { ausruestungID := c.Param("ausruestung_id") - if err := database.DB.Delete(&models.EqAusruestung{}, ausruestungID).Error; err != nil { + + var ausruestung models.EqAusruestung + if err := database.DB.First(&ausruestung, ausruestungID).Error; err != nil { + respondWithError(c, http.StatusNotFound, "Ausruestung not found") + return + } + + // Check ownership + if !checkEquipmentOwnership(c, ausruestung.CharacterID) { + return + } + + if err := database.DB.Delete(&ausruestung).Error; err != nil { respondWithError(c, http.StatusInternalServerError, "Failed to delete Ausruestung") return } @@ -89,6 +126,11 @@ func CreateWaffe(c *gin.Context) { return } + // Check ownership + if !checkEquipmentOwnership(c, waffe.CharacterID) { + return + } + if err := database.DB.Create(&waffe).Error; err != nil { respondWithError(c, http.StatusInternalServerError, "Failed to create Waffe") return @@ -118,6 +160,11 @@ func UpdateWaffe(c *gin.Context) { return } + // Check ownership + if !checkEquipmentOwnership(c, waffe.CharacterID) { + return + } + if err := c.ShouldBindJSON(&waffe); err != nil { respondWithError(c, http.StatusBadRequest, err.Error()) return @@ -133,7 +180,19 @@ func UpdateWaffe(c *gin.Context) { func DeleteWaffe(c *gin.Context) { waffeID := c.Param("waffe_id") - if err := database.DB.Delete(&models.EqWaffe{}, waffeID).Error; err != nil { + + var waffe models.EqWaffe + if err := database.DB.First(&waffe, waffeID).Error; err != nil { + respondWithError(c, http.StatusNotFound, "Waffe not found") + return + } + + // Check ownership + if !checkEquipmentOwnership(c, waffe.CharacterID) { + return + } + + if err := database.DB.Delete(&waffe).Error; err != nil { respondWithError(c, http.StatusInternalServerError, "Failed to delete Waffe") return } diff --git a/backend/gsmaster/routes.go b/backend/gsmaster/routes.go index 02d8e5a..be9c905 100644 --- a/backend/gsmaster/routes.go +++ b/backend/gsmaster/routes.go @@ -8,50 +8,52 @@ import ( func RegisterRoutes(r *gin.RouterGroup) { maintGrp := r.Group("/maintenance") + + maintGrp.GET("", GetMasterData) + maintGrp.GET("/skills", GetMDSkills) + maintGrp.GET("/skills-enhanced", GetEnhancedMDSkills) // New enhanced endpoint + maintGrp.GET("/skills/:id", GetMDSkill) + maintGrp.GET("/skills-enhanced/:id", GetEnhancedMDSkill) // New enhanced endpoint + maintGrp.GET("/weaponskills", GetMDWeaponSkills) + maintGrp.GET("/weaponskills-enhanced", GetEnhancedMDWeaponSkills) // New enhanced endpoint + maintGrp.GET("/weaponskills/:id", GetMDWeaponSkill) + maintGrp.GET("/weaponskills-enhanced/:id", GetEnhancedMDWeaponSkill) // New enhanced endpoint + maintGrp.GET("/spells", GetMDSpells) + maintGrp.GET("/spells-enhanced", GetEnhancedMDSpells) // New enhanced endpoint + maintGrp.GET("/spells/:id", GetMDSpell) + maintGrp.GET("/spells-enhanced/:id", GetEnhancedMDSpell) // New enhanced endpoint + maintGrp.GET("/equipment", GetMDEquipments) + maintGrp.GET("/equipment-enhanced", GetEnhancedMDEquipment) // New enhanced endpoint + maintGrp.GET("/equipment/:id", GetMDEquipment) + maintGrp.GET("/equipment-enhanced/:id", GetEnhancedMDEquipmentItem) // New enhanced endpoint + maintGrp.GET("/weapons", GetMDWeapons) + maintGrp.GET("/weapons-enhanced", GetEnhancedMDWeapons) // New enhanced endpoint + maintGrp.GET("/weapons/:id", GetMDWeapon) + maintGrp.GET("/weapons-enhanced/:id", GetEnhancedMDWeapon) // New enhanced endpoint + maintGrp.Use(user.RequireMaintainer()) { - maintGrp.GET("", GetMasterData) - maintGrp.GET("/skills", GetMDSkills) - maintGrp.GET("/skills-enhanced", GetEnhancedMDSkills) // New enhanced endpoint maintGrp.POST("/skills-enhanced", CreateEnhancedMDSkill) // Create new skill - maintGrp.GET("/skills/:id", GetMDSkill) - maintGrp.GET("/skills-enhanced/:id", GetEnhancedMDSkill) // New enhanced endpoint maintGrp.PUT("/skills/:id", UpdateMDSkill) maintGrp.PUT("/skills-enhanced/:id", UpdateEnhancedMDSkill) // New enhanced endpoint maintGrp.POST("/skills", AddSkill) maintGrp.DELETE("/skills/:id", DeleteMDSkill) - maintGrp.GET("/weaponskills", GetMDWeaponSkills) - maintGrp.GET("/weaponskills-enhanced", GetEnhancedMDWeaponSkills) // New enhanced endpoint - maintGrp.GET("/weaponskills/:id", GetMDWeaponSkill) - maintGrp.GET("/weaponskills-enhanced/:id", GetEnhancedMDWeaponSkill) // New enhanced endpoint maintGrp.PUT("/weaponskills/:id", UpdateMDWeaponSkill) maintGrp.PUT("/weaponskills-enhanced/:id", UpdateEnhancedMDWeaponSkill) // New enhanced endpoint maintGrp.POST("/weaponskills", AddWeaponSkill) maintGrp.DELETE("/weaponskills/:id", DeleteMDWeaponSkill) - maintGrp.GET("/spells", GetMDSpells) - maintGrp.GET("/spells-enhanced", GetEnhancedMDSpells) // New enhanced endpoint - maintGrp.GET("/spells/:id", GetMDSpell) - maintGrp.GET("/spells-enhanced/:id", GetEnhancedMDSpell) // New enhanced endpoint maintGrp.PUT("/spells/:id", UpdateMDSpell) maintGrp.PUT("/spells-enhanced/:id", UpdateEnhancedMDSpell) // New enhanced endpoint maintGrp.POST("/spells", AddSpell) maintGrp.DELETE("/spells/:id", DeleteMDSpell) - maintGrp.GET("/equipment", GetMDEquipments) - maintGrp.GET("/equipment-enhanced", GetEnhancedMDEquipment) // New enhanced endpoint - maintGrp.GET("/equipment/:id", GetMDEquipment) - maintGrp.GET("/equipment-enhanced/:id", GetEnhancedMDEquipmentItem) // New enhanced endpoint maintGrp.PUT("/equipment/:id", UpdateMDEquipment) maintGrp.PUT("/equipment-enhanced/:id", UpdateEnhancedMDEquipmentItem) // New enhanced endpoint maintGrp.POST("/equipment", AddEquipment) maintGrp.DELETE("/equipment/:id", DeleteMDEquipment) - maintGrp.GET("/weapons", GetMDWeapons) - maintGrp.GET("/weapons-enhanced", GetEnhancedMDWeapons) // New enhanced endpoint - maintGrp.GET("/weapons/:id", GetMDWeapon) - maintGrp.GET("/weapons-enhanced/:id", GetEnhancedMDWeapon) // New enhanced endpoint maintGrp.PUT("/weapons/:id", UpdateMDWeapon) maintGrp.PUT("/weapons-enhanced/:id", UpdateEnhancedMDWeapon) // New enhanced endpoint maintGrp.POST("/weapons", AddWeapon) diff --git a/frontend/src/components/CharacterDetails.vue b/frontend/src/components/CharacterDetails.vue index 3ae6682..275f1b0 100644 --- a/frontend/src/components/CharacterDetails.vue +++ b/frontend/src/components/CharacterDetails.vue @@ -6,7 +6,7 @@ -

{{ $t('char') }}: {{ character.name }} ({{ $t(currentView) }})

@@ -32,7 +32,7 @@ - + @@ -64,6 +64,7 @@