193 lines
5.7 KiB
Go
193 lines
5.7 KiB
Go
|
|
package admin
|
||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/json"
|
||
|
|
"net/http"
|
||
|
|
"strconv"
|
||
|
|
"strings"
|
||
|
|
|
||
|
|
"epigas.gitea.cloud/RiskRancher/core/pkg/auth"
|
||
|
|
)
|
||
|
|
|
||
|
|
// PasswordResetRequest is the expected JSON payload
|
||
|
|
type PasswordResetRequest struct {
|
||
|
|
NewPassword string `json:"new_password"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// HandleAdminResetPassword allows a Sheriff to forcefully overwrite a user's password.
|
||
|
|
func (h *Handler) HandleAdminResetPassword(w http.ResponseWriter, r *http.Request) {
|
||
|
|
idStr := r.PathValue("id")
|
||
|
|
userID, err := strconv.Atoi(idStr)
|
||
|
|
if err != nil {
|
||
|
|
http.Error(w, "Invalid user ID in URL", http.StatusBadRequest)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
var req PasswordResetRequest
|
||
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||
|
|
http.Error(w, "Invalid JSON payload", http.StatusBadRequest)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if req.NewPassword == "" {
|
||
|
|
http.Error(w, "New password cannot be empty", http.StatusBadRequest)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
hashedPassword, err := auth.HashPassword(req.NewPassword)
|
||
|
|
if err != nil {
|
||
|
|
http.Error(w, "Internal server error during hashing", http.StatusInternalServerError)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
err = h.Store.UpdateUserPassword(r.Context(), userID, hashedPassword)
|
||
|
|
if err != nil {
|
||
|
|
http.Error(w, "Failed to update user password", http.StatusInternalServerError)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
w.WriteHeader(http.StatusOK)
|
||
|
|
json.NewEncoder(w).Encode(map[string]string{
|
||
|
|
"message": "Password reset successfully",
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
type RoleUpdateRequest struct {
|
||
|
|
GlobalRole string `json:"global_role"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// HandleUpdateUserRole allows a Sheriff to promote or demote a user.
|
||
|
|
func (h *Handler) HandleUpdateUserRole(w http.ResponseWriter, r *http.Request) {
|
||
|
|
idStr := r.PathValue("id")
|
||
|
|
userID, err := strconv.Atoi(idStr)
|
||
|
|
if err != nil {
|
||
|
|
http.Error(w, "Invalid user ID in URL", http.StatusBadRequest)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
var req RoleUpdateRequest
|
||
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||
|
|
http.Error(w, "Invalid JSON payload", http.StatusBadRequest)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
validRoles := map[string]bool{
|
||
|
|
"Sheriff": true, "Wrangler": true, "RangeHand": true, "CircuitRider": true, "Magistrate": true,
|
||
|
|
}
|
||
|
|
if !validRoles[req.GlobalRole] {
|
||
|
|
http.Error(w, "Invalid role provided", http.StatusBadRequest)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
err = h.Store.UpdateUserRole(r.Context(), userID, req.GlobalRole)
|
||
|
|
if err != nil {
|
||
|
|
http.Error(w, "Failed to update user role", http.StatusInternalServerError)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
w.WriteHeader(http.StatusOK)
|
||
|
|
json.NewEncoder(w).Encode(map[string]string{
|
||
|
|
"message": "User role updated successfully to " + req.GlobalRole,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
// HandleDeactivateUser allows a Sheriff to safely offboard a user.
|
||
|
|
func (h *Handler) HandleDeactivateUser(w http.ResponseWriter, r *http.Request) {
|
||
|
|
idStr := r.PathValue("id")
|
||
|
|
userID, err := strconv.Atoi(idStr)
|
||
|
|
if err != nil {
|
||
|
|
http.Error(w, "Invalid user ID in URL", http.StatusBadRequest)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
err = h.Store.DeactivateUserAndReassign(r.Context(), userID)
|
||
|
|
if err != nil {
|
||
|
|
http.Error(w, "Failed to deactivate user", http.StatusInternalServerError)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
w.WriteHeader(http.StatusOK)
|
||
|
|
json.NewEncoder(w).Encode(map[string]string{
|
||
|
|
"message": "User successfully deactivated and tickets reassigned.",
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
// CreateUserRequest is the payload the Sheriff sends to invite a new user
|
||
|
|
type CreateUserRequest struct {
|
||
|
|
Email string `json:"email"`
|
||
|
|
FullName string `json:"full_name"`
|
||
|
|
Password string `json:"password"`
|
||
|
|
GlobalRole string `json:"global_role"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// HandleCreateUser allows a Sheriff to manually provision a new user account.
|
||
|
|
func (h *Handler) HandleCreateUser(w http.ResponseWriter, r *http.Request) {
|
||
|
|
var req CreateUserRequest
|
||
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||
|
|
http.Error(w, "Invalid JSON payload", http.StatusBadRequest)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if req.Email == "" || req.FullName == "" || req.Password == "" || req.GlobalRole == "" {
|
||
|
|
http.Error(w, "Missing required fields", http.StatusBadRequest)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
validRoles := map[string]bool{
|
||
|
|
"Sheriff": true, "Wrangler": true, "RangeHand": true, "CircuitRider": true, "Magistrate": true,
|
||
|
|
}
|
||
|
|
if !validRoles[req.GlobalRole] {
|
||
|
|
http.Error(w, "Invalid role provided", http.StatusBadRequest)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
hashedPassword, err := auth.HashPassword(req.Password)
|
||
|
|
if err != nil {
|
||
|
|
http.Error(w, "Internal server error during hashing", http.StatusInternalServerError)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
user, err := h.Store.CreateUser(r.Context(), req.Email, req.FullName, hashedPassword, req.GlobalRole)
|
||
|
|
if err != nil {
|
||
|
|
if strings.Contains(err.Error(), "UNIQUE constraint failed") {
|
||
|
|
http.Error(w, "Email already exists in the system", http.StatusConflict)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
http.Error(w, "Failed to provision user", http.StatusInternalServerError)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
w.WriteHeader(http.StatusCreated)
|
||
|
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
||
|
|
"message": "User provisioned successfully. Share the temporary password securely.",
|
||
|
|
"id": user.ID,
|
||
|
|
"email": user.Email,
|
||
|
|
"full_name": user.FullName,
|
||
|
|
"global_role": user.GlobalRole,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
// HandleGetUsers returns a list of all users in the system for the Sheriff to manage.
|
||
|
|
func (h *Handler) HandleGetUsers(w http.ResponseWriter, r *http.Request) {
|
||
|
|
users, err := h.Store.GetAllUsers(r.Context())
|
||
|
|
if err != nil {
|
||
|
|
http.Error(w, "Failed to fetch user roster", http.StatusInternalServerError)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
w.Header().Set("Content-Type", "application/json")
|
||
|
|
w.WriteHeader(http.StatusOK)
|
||
|
|
json.NewEncoder(w).Encode(users)
|
||
|
|
}
|
||
|
|
|
||
|
|
// HandleGetWranglers returns a clean list of IT users for assignment dropdowns
|
||
|
|
func (h *Handler) HandleGetWranglers(w http.ResponseWriter, r *http.Request) {
|
||
|
|
wranglers, err := h.Store.GetWranglers(r.Context())
|
||
|
|
if err != nil {
|
||
|
|
http.Error(w, "Failed to fetch wranglers", http.StatusInternalServerError)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
w.Header().Set("Content-Type", "application/json")
|
||
|
|
json.NewEncoder(w).Encode(wranglers)
|
||
|
|
}
|