diff --git a/backend/handlers/handle_user.go b/backend/handlers/handle_user.go index 6293c9a..27b53b4 100644 --- a/backend/handlers/handle_user.go +++ b/backend/handlers/handle_user.go @@ -14,6 +14,10 @@ type UserCredentials struct { Password string `json:"password" binding:"required"` } +type SetAdminInput struct { + Admin *bool `json:"admin" binding:"required"` +} + func (store *Store) CreateUser(ctx *gin.Context) { claimsVal, ok := ctx.Get("userClaims") if !ok { @@ -101,6 +105,57 @@ func (store *Store) UpdateUser(ctx *gin.Context) { ctx.JSON(http.StatusInternalServerError, gin.H{"error": "will be implemented"}) } +func (store *Store) SetUserAdmin(ctx *gin.Context) { + claimsVal, ok := ctx.Get("userClaims") + if !ok { + ctx.JSON(http.StatusUnauthorized, gin.H{"error": "user claims could not be found"}) + return + } + claims, ok := claimsVal.(*jwt.MapClaims) + if !ok { + ctx.JSON(http.StatusInternalServerError, gin.H{"error": "invalid claims"}) + return + } + if !(*claims)["admin"].(bool) { + ctx.JSON(http.StatusForbidden, gin.H{"error": "admin access required"}) + return + } + + callerIDF, ok := (*claims)["id"].(float64) + if !ok { + ctx.JSON(http.StatusInternalServerError, gin.H{"error": "invalid user id in claims"}) + return + } + callerID := uint(callerIDF) + + targetID := ctx.Param("id") + + var input SetAdminInput + if err := ctx.ShouldBindBodyWithJSON(&input); err != nil { + ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + var user models.User + if err := store.DB.First(&user, targetID).Error; err != nil { + ctx.JSON(http.StatusNotFound, gin.H{"error": "user not found"}) + return + } + + if user.ID == callerID { + ctx.JSON(http.StatusBadRequest, gin.H{"error": "cannot change your own admin status"}) + return + } + + user.Admin = *input.Admin + if err := store.DB.Save(&user).Error; err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + ctx.JSON(http.StatusOK, user) +} + func (store *Store) DeleteUser(ctx *gin.Context) { claimsVal, ok := ctx.Get("userClaims") if !ok { diff --git a/backend/main.go b/backend/main.go index 56dfaac..fb038bd 100644 --- a/backend/main.go +++ b/backend/main.go @@ -97,6 +97,7 @@ func main() { protected.DELETE("/user/:id", store.DeleteUser) r.GET("/user", store.GetUsers) protected.POST("/user", store.CreateUser) + protected.PATCH("/user/:id/admin", store.SetUserAdmin) // AUTH r.POST("/auth/login", store.Login) diff --git a/nginx/vue/src/stores/auth.js b/nginx/vue/src/stores/auth.js index 93110e3..e6ab2f1 100644 --- a/nginx/vue/src/stores/auth.js +++ b/nginx/vue/src/stores/auth.js @@ -59,6 +59,16 @@ export const useAuthStore = defineStore("auth", () => { } } + async function setUserAdmin(userId, admin) { + try { + const res = await axios.patch(`/api/user/${userId}/admin`, { admin }); + return res.data; + } catch (err) { + console.error(err); + throw err; + } + } + return { user, @@ -69,5 +79,6 @@ export const useAuthStore = defineStore("auth", () => { refreshToken, logOut, createUser, + setUserAdmin, }; }); diff --git a/nginx/vue/src/views/admin/Admin.vue b/nginx/vue/src/views/admin/Admin.vue index 7fba5fc..e0a58cc 100644 --- a/nginx/vue/src/views/admin/Admin.vue +++ b/nginx/vue/src/views/admin/Admin.vue @@ -8,6 +8,7 @@ import CreatePost from "./CreatePost.vue"; import CreateFavorite from "./CreateFavorite.vue"; import CreateActivity from "./CreateActivity.vue"; import CreateRowing from "./CreateRowing.vue"; +import ManageUsers from "./ManageUsers.vue"; const auth = useAuthStore(); @@ -21,6 +22,7 @@ const auth = useAuthStore(); + diff --git a/nginx/vue/src/views/admin/ManageUsers.vue b/nginx/vue/src/views/admin/ManageUsers.vue new file mode 100644 index 0000000..d3541a2 --- /dev/null +++ b/nginx/vue/src/views/admin/ManageUsers.vue @@ -0,0 +1,45 @@ + + +