137 lines
5.9 KiB
Go
137 lines
5.9 KiB
Go
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)
|
|
}
|