package api_acladmin import ( "encoding/json" "log/slog" "net/http" "strconv" "git.oblat.lv/alex/triggerssmith/internal/acl" "github.com/go-chi/chi/v5" ) // @Summary Get user roles by user ID // @Tags acl/users // @Produce json // @Param userId path int true "User ID" example(1) // @Success 200 {object} getUserRolesResponse // @Failure 400 {object} ProblemDetails // @Failure 404 {object} ProblemDetails // @Failure 500 {object} ProblemDetails // @Router /api/acl/users/{userId}/roles [get] func (h *aclAdminHandler) getUserRoles(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") userIDStr := chi.URLParam(r, "userId") userID, err := strconv.Atoi(userIDStr) if err != nil { writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-user-id", "Invalid user ID", "User ID must be positive integer", r) return } roles, err := h.a.GetUserRoles(uint(userID)) if err != nil { switch err { case acl.ErrNotInitialized: writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r) case acl.ErrUserNotFound: writeProblem(w, http.StatusNotFound, "/errors/acl/user-not-found", "User not found", "User not found", r) case acl.ErrRoleNotFound: writeProblem(w, http.StatusNotFound, "/errors/acl/no-role-found", "No role found", "No role found for user "+strconv.Itoa(userID), r) default: slog.Error("unexpected server error", "error", err.Error()) writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) } return } resp := make(getUserRolesResponse, 0, len(roles)) for _, role := range roles { resp = append(resp, getUserRole{ID: role.ID, Name: role.Name}) } _ = json.NewEncoder(w).Encode(resp) } // @Summary Assign role to user // @Tags acl/users // @Produce json // @Param userId path int true "User ID" example(1) // @Param body body assignRoleToUserRequest true "Role ID" // @Success 201 // @Failure 400 {object} ProblemDetails // @Failure 404 {object} ProblemDetails // @Failure 409 {object} ProblemDetails // @Failure 500 {object} ProblemDetails // @Router /api/acl/users/{userId}/roles [post] func (h *aclAdminHandler) assignRoleToUser(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") userIDStr := chi.URLParam(r, "userId") userID, err := strconv.Atoi(userIDStr) if err != nil || userID < 0 { writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-user-id", "Invalid user ID", "User ID must be positive integer", r) return } var req assignRoleToUserRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-request-body", "Invalid request body", "Invalid JSON body", r) return } if err := h.a.AssignRoleToUser(req.RoleID, uint(userID)); err != nil { slog.Error("Failed to assign role to user", "error", err.Error()) switch err { case acl.ErrNotInitialized: writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r) case acl.ErrUserNotFound: writeProblem(w, http.StatusNotFound, "/errors/acl/user-not-found", "User not found", "User not found", r) case acl.ErrRoleNotFound: writeProblem(w, http.StatusNotFound, "/errors/acl/no-role-found", "No role found", "No role found for user "+strconv.Itoa(userID), r) case acl.ErrRoleAlreadyAssigned: writeProblem(w, http.StatusConflict, "/errors/acl/role-already-assigned", "Role already assigned", "Role with ID "+strconv.Itoa(int(req.RoleID))+" is already assigned to user "+strconv.Itoa(userID), r) default: writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) } return } w.WriteHeader(http.StatusCreated) } // @Summary Remove role from user // @Tags acl/users // @Produce json // @Param userId path int true "User ID" example(1) // @Param roleId path int true "Role ID" example(1) // @Success 204 // @Failure 400 {object} ProblemDetails // @Failure 404 {object} ProblemDetails // @Failure 500 {object} ProblemDetails // @Router /api/acl/users/{userId}/roles/{roleId} [delete] func (h *aclAdminHandler) removeRoleFromUser(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") userIDStr := chi.URLParam(r, "userId") userID, err := strconv.Atoi(userIDStr) if err != nil || userID < 0 { writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-user-id", "Invalid user ID", "User ID must be positive integer", r) return } roleIDStr := chi.URLParam(r, "roleId") roleID, err := strconv.Atoi(roleIDStr) if err != nil || roleID < 0 { writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-role-id", "Invalid role ID", "Role ID must be positive integer", r) return } err = h.a.RemoveRoleFromUser(uint(roleID), uint(userID)) if err != nil { slog.Error("Failed to remove role from user", "error", err.Error()) switch err { case acl.ErrNotInitialized: writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r) case acl.ErrUserNotFound: writeProblem(w, http.StatusNotFound, "/errors/acl/user-not-found", "User not found", "User not found", r) case acl.ErrRoleNotFound: writeProblem(w, http.StatusNotFound, "/errors/acl/no-role-found", "No role found", "No role found for user "+strconv.Itoa(userID), r) case acl.ErrUserRoleNotFound: writeProblem(w, http.StatusNotFound, "/errors/acl/user-role-not-found", "User role not found", "User "+strconv.Itoa(userID)+" does not have role "+strconv.Itoa(roleID), r) default: writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r) } } w.WriteHeader(http.StatusNoContent) }