some changes

This commit is contained in:
2025-12-21 00:00:03 +02:00
parent 904f446447
commit 85f8ac60e7
14 changed files with 678 additions and 1475 deletions

View File

@@ -1,5 +1,11 @@
package api_acladmin package api_acladmin
import (
"encoding/json"
"log/slog"
"net/http"
)
const ( const (
ErrorInvalidRequestBody = "INVALID_REQUEST_BODY" ErrorInvalidRequestBody = "INVALID_REQUEST_BODY"
ErrorInternalServerError = "INTERNAL_SERVER_ERROR" ErrorInternalServerError = "INTERNAL_SERVER_ERROR"
@@ -26,3 +32,28 @@ const (
const ( const (
ErrorACLServiceNotInitialized = "ACL service is not initialized" ErrorACLServiceNotInitialized = "ACL service is not initialized"
) )
// RFC-7807 (Problem Details)
type ProblemDetails struct {
Type string `json:"type" example:"https://api.triggerssmith.com/errors/role-not-found"`
Title string `json:"title" example:"Role not found"`
Status int `json:"status" example:"404"`
Detail string `json:"detail" example:"No role with ID 42"`
Instance string `json:"instance" example:"/api/acl/roles/42"`
}
var typeDomain = "https://api.triggerssmith.com"
func writeProblem(w http.ResponseWriter, status int, typ, title, detail string, r *http.Request) {
w.Header().Set("Content-Type", "application/problem+json")
w.WriteHeader(status)
prob := ProblemDetails{
Type: typeDomain + typ,
Title: title,
Status: status,
Detail: detail,
Instance: r.URL.Path,
}
slog.Warn("new problem", "type", typ, "title", title, "detail", detail, "instance", r.URL.Path, "status", status)
_ = json.NewEncoder(w).Encode(prob)
}

View File

@@ -54,6 +54,7 @@ func MustRoute(config *config.Config, aclService *acl.Service, authService *auth
r.Get("/roles", h.getRoles) // list all roles r.Get("/roles", h.getRoles) // list all roles
r.Post("/roles", h.createRole) // create a new role r.Post("/roles", h.createRole) // create a new role
r.Get("/roles/{roleId}", h.getRole) // get a role by ID r.Get("/roles/{roleId}", h.getRole) // get a role by ID
r.Get("/roles/{roleId}/users", h.getRoleUsers) // get all assigned users to a role
r.Patch("/roles/{roleId}", h.updateRole) // update a role by ID r.Patch("/roles/{roleId}", h.updateRole) // update a role by ID
r.Delete("/roles/{roleId}", h.deleteRole) // delete a role by ID r.Delete("/roles/{roleId}", h.deleteRole) // delete a role by ID

View File

@@ -13,44 +13,35 @@ import (
// @Summary Get all resources // @Summary Get all resources
// @Tags resources // @Tags resources
// @Produce json // @Produce json
// @Success 200 {object} getResourcesResponse // @Success 200 {array} getResourcesResponse
// @Failure 500 {object} errorInternalServerError // @Failure 500 {object} ProblemDetails
// @Router /api/acl/resources [get] // @Router /api/acl/resources [get]
func (h *aclAdminHandler) getResources(w http.ResponseWriter, r *http.Request) { func (h *aclAdminHandler) getResources(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
resources, err := h.a.GetResources() resources, err := h.a.GetResources()
if err != nil { if err != nil {
switch err { switch err {
case acl.ErrNotInitialized: case acl.ErrNotInitialized:
w.WriteHeader(http.StatusInternalServerError) writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "acl service is not initialized", r)
_ = json.NewEncoder(w).Encode(errorInternalServerError{
Error: ErrorInternalServerError,
Details: ErrorACLServiceNotInitialized,
})
return
default: default:
w.WriteHeader(http.StatusInternalServerError) slog.Error("unexpected server error", "error", err.Error())
_ = json.NewEncoder(w).Encode(errorInternalServerError{ writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r)
Error: ErrorInternalServerError,
Details: "Failed to get resources",
})
return
} }
return
} }
_ = json.NewEncoder(w).Encode(func() getResourcesResponse { type R struct {
resp := make(getResourcesResponse, 0, len(resources))
for _, res := range resources {
resp = append(resp, struct {
ID uint `json:"id" example:"1"` ID uint `json:"id" example:"1"`
Key string `json:"key" example:"html.view"` Key string `json:"key" example:"html.view"`
}{
ID: res.ID,
Key: res.Key,
})
} }
return resp
}()) resp := make([]R, 0, len(resources))
for _, res := range resources {
resp = append(resp, R{ID: res.ID, Key: res.Key})
}
_ = json.NewEncoder(w).Encode(resp)
} }
// @Summary Get resource by ID // @Summary Get resource by ID
@@ -58,20 +49,17 @@ func (h *aclAdminHandler) getResources(w http.ResponseWriter, r *http.Request) {
// @Produce json // @Produce json
// @Param resourceId path int true "Resource ID" example(1) // @Param resourceId path int true "Resource ID" example(1)
// @Success 200 {object} getResourceResponse // @Success 200 {object} getResourceResponse
// @Failure 400 {object} getResourceErrorInvalidResourceID // @Failure 400 {object} ProblemDetails
// @Failure 404 {object} getResourceErrorResourceNotFound // @Failure 404 {object} ProblemDetails
// @Failure 500 {object} errorInternalServerError // @Failure 500 {object} ProblemDetails
// @Router /api/acl/resources/{resourceId} [get] // @Router /api/acl/resources/{resourceId} [get]
func (h *aclAdminHandler) getResource(w http.ResponseWriter, r *http.Request) { func (h *aclAdminHandler) getResource(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
resourceIDStr := chi.URLParam(r, "resourceId") resourceIDStr := chi.URLParam(r, "resourceId")
resourceID, err := strconv.Atoi(resourceIDStr) resourceID, err := strconv.Atoi(resourceIDStr)
if err != nil || resourceID < 0 { if err != nil || resourceID < 0 {
w.WriteHeader(http.StatusBadRequest) writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-resource-id", "Invalid resource ID", "Resource ID must be positive integer", r)
_ = json.NewEncoder(w).Encode(getResourceErrorInvalidResourceID{
Error: ErrorInvalidResourceID,
Details: "Resource ID must be positive integer",
})
return return
} }
@@ -79,27 +67,14 @@ func (h *aclAdminHandler) getResource(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
switch err { switch err {
case acl.ErrNotInitialized: case acl.ErrNotInitialized:
w.WriteHeader(http.StatusInternalServerError) writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "acl service is not initialized", r)
_ = json.NewEncoder(w).Encode(errorInternalServerError{
Error: ErrorInternalServerError,
Details: ErrorACLServiceNotInitialized,
})
return
case acl.ErrResourceNotFound: case acl.ErrResourceNotFound:
w.WriteHeader(http.StatusNotFound) writeProblem(w, http.StatusNotFound, "/errors/acl/resource-not-found", "Resource not found", "No resource with ID "+resourceIDStr, r)
_ = json.NewEncoder(w).Encode(getResourceErrorResourceNotFound{
Error: ErrorResourceNotFound,
Details: "No resource with ID " + resourceIDStr,
})
return
default: default:
w.WriteHeader(http.StatusInternalServerError) slog.Error("unexpected server error", "error", err.Error())
_ = json.NewEncoder(w).Encode(errorInternalServerError{ writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r)
Error: ErrorInternalServerError,
Details: "Failed to get resource with ID " + resourceIDStr,
})
return
} }
return
} }
_ = json.NewEncoder(w).Encode(getResourceResponse{ _ = json.NewEncoder(w).Encode(getResourceResponse{
@@ -114,56 +89,35 @@ func (h *aclAdminHandler) getResource(w http.ResponseWriter, r *http.Request) {
// @Produce json // @Produce json
// @Param request body createResourceRequest true "Resource" // @Param request body createResourceRequest true "Resource"
// @Success 201 {object} createResourceResponse // @Success 201 {object} createResourceResponse
// @Failure 400 {object} errorInvalidRequestBody // @Failure 400 {object} ProblemDetails
// @Failure 400 {object} createResourceErrorInvalidResourceKey // @Failure 409 {object} ProblemDetails
// @Failure 409 {object} createResourceErrorResourceAlreadyExists // @Failure 500 {object} ProblemDetails
// @Failure 500 {object} errorInternalServerError
// @Router /api/acl/resources [post] // @Router /api/acl/resources [post]
func (h *aclAdminHandler) createResource(w http.ResponseWriter, r *http.Request) { func (h *aclAdminHandler) createResource(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
var req createResourceRequest var req createResourceRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil { if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
w.WriteHeader(http.StatusBadRequest) writeProblem(w, http.StatusBadRequest, "/errors/invalid-request-body", "Invalid request body", "Body is not valid JSON", r)
_ = json.NewEncoder(w).Encode(errorInvalidRequestBody{
Error: ErrorInvalidRequestBody,
Details: "Request body is not valid JSON",
})
return return
} }
resourceID, err := h.a.CreateResource(req.Key) resourceID, err := h.a.CreateResource(req.Key)
if err != nil { if err != nil {
slog.Error("Failed to create resource", "error", err.Error()) slog.Error("Failed to create resource", "error", err)
switch err { switch err {
case acl.ErrNotInitialized: case acl.ErrNotInitialized:
w.WriteHeader(http.StatusInternalServerError) writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "acl service is not initialized", r)
_ = json.NewEncoder(w).Encode(errorInternalServerError{
Error: ErrorInternalServerError,
Details: ErrorACLServiceNotInitialized,
})
return
case acl.ErrInvalidResourceKey: case acl.ErrInvalidResourceKey:
w.WriteHeader(http.StatusBadRequest) writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-resource-key", "Invalid resource key", "Resource key must be non-empty", r)
_ = json.NewEncoder(w).Encode(createResourceErrorInvalidResourceKey{
Error: ErrorFailedToCreateResource,
Details: "Resource key must be non-empty",
})
return
case acl.ErrResourceAlreadyExists: case acl.ErrResourceAlreadyExists:
w.WriteHeader(http.StatusConflict) writeProblem(w, http.StatusConflict, "/errors/acl/resource-already-exists", "Resource already exists", "Resource '"+req.Key+"' already exists", r)
_ = json.NewEncoder(w).Encode(createResourceErrorResourceAlreadyExists{
Error: ErrorFailedToCreateResource,
Details: "Resource with key '" + req.Key + "' already exists",
})
return
default: default:
w.WriteHeader(http.StatusInternalServerError) slog.Error("unexpected server error", "error", err.Error())
_ = json.NewEncoder(w).Encode(errorInternalServerError{ writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r)
Error: ErrorInternalServerError,
Details: "Failed to create resource with key '" + req.Key + "'",
})
return
} }
return
} }
w.WriteHeader(http.StatusCreated) w.WriteHeader(http.StatusCreated)
@@ -180,79 +134,47 @@ func (h *aclAdminHandler) createResource(w http.ResponseWriter, r *http.Request)
// @Param resourceId path int true "Resource ID" example(1) // @Param resourceId path int true "Resource ID" example(1)
// @Param request body updateResourceRequest true "Resource" // @Param request body updateResourceRequest true "Resource"
// @Success 200 {object} updateResourceResponse // @Success 200 {object} updateResourceResponse
// @Failure 400 {object} errorInvalidRequestBody // @Failure 400 {object} ProblemDetails
// @Failure 400 {object} updateResourceErrorInvalidResourceID // @Failure 404 {object} ProblemDetails
// @Failure 400 {object} updateResourceErrorInvalidResourceKey // @Failure 409 {object} ProblemDetails
// @Failure 404 {object} updateResourceErrorResourceNotFound // @Failure 500 {object} ProblemDetails
// @Failure 409 {object} updateResourceErrorResourceKeyAlreadyExists
// @Failure 500 {object} errorInternalServerError
// @Router /api/acl/resources/{resourceId} [patch] // @Router /api/acl/resources/{resourceId} [patch]
func (h *aclAdminHandler) updateResource(w http.ResponseWriter, r *http.Request) { func (h *aclAdminHandler) updateResource(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
var req updateResourceRequest var req updateResourceRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil { if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
w.WriteHeader(http.StatusBadRequest) writeProblem(w, http.StatusBadRequest, "/errors/invalid-request-body", "Invalid request body", "Body is not valid JSON", r)
_ = json.NewEncoder(w).Encode(errorInvalidRequestBody{
Error: ErrorInvalidRequestBody,
Details: "Request body is not valid JSON",
})
return return
} }
resourceIDStr := chi.URLParam(r, "resourceId") resourceIDStr := chi.URLParam(r, "resourceId")
resourceID, err := strconv.Atoi(resourceIDStr) resourceID, err := strconv.Atoi(resourceIDStr)
if err != nil || resourceID < 0 { if err != nil || resourceID < 0 {
w.WriteHeader(http.StatusBadRequest) writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-resource-id", "Invalid resource ID", "Resource ID must be positive integer", r)
_ = json.NewEncoder(w).Encode(updateResourceErrorInvalidResourceID{
Error: ErrorInvalidResourceID,
Details: "Resource ID must be positive integer",
})
return return
} }
err = h.a.UpdateResource(uint(resourceID), req.Key) err = h.a.UpdateResource(uint(resourceID), req.Key)
if err != nil { if err != nil {
slog.Error("Failed to update resource", "error", err.Error()) slog.Error("Failed to update resource", "error", err)
switch err { switch err {
case acl.ErrNotInitialized: case acl.ErrNotInitialized:
w.WriteHeader(http.StatusInternalServerError) writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "acl service is not initialized", r)
_ = json.NewEncoder(w).Encode(errorInternalServerError{
Error: ErrorInternalServerError,
Details: ErrorACLServiceNotInitialized,
})
return
case acl.ErrInvalidResourceKey: case acl.ErrInvalidResourceKey:
w.WriteHeader(http.StatusBadRequest) writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-resource-key", "Invalid resource key", "Resource key must be non-empty", r)
_ = json.NewEncoder(w).Encode(updateResourceErrorInvalidResourceKey{
Error: ErrorFailedToUpdateResource,
Details: "Invalid resource key",
})
return
case acl.ErrResourceNotFound: case acl.ErrResourceNotFound:
w.WriteHeader(http.StatusNotFound) writeProblem(w, http.StatusNotFound, "/errors/acl/resource-not-found", "Resource not found", "No resource with ID "+resourceIDStr, r)
_ = json.NewEncoder(w).Encode(updateResourceErrorResourceNotFound{
Error: ErrorFailedToUpdateResource,
Details: "No resource with ID " + resourceIDStr,
})
return
case acl.ErrSameResourceKey: case acl.ErrSameResourceKey:
w.WriteHeader(http.StatusConflict) writeProblem(w, http.StatusConflict, "/errors/acl/resource-key-already-exists", "Resource key already exists", "Resource key '"+req.Key+"' already exists", r)
_ = json.NewEncoder(w).Encode(updateResourceErrorResourceKeyAlreadyExists{
Error: ErrorFailedToUpdateResource,
Details: "Resource with key '" + req.Key + "' already exists",
})
return
default: default:
w.WriteHeader(http.StatusInternalServerError) slog.Error("unexpected server error", "error", err.Error())
_ = json.NewEncoder(w).Encode(errorInternalServerError{ writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r)
Error: ErrorInternalServerError,
Details: "Failed to update resource with key '" + req.Key + "'",
})
return
} }
return
} }
w.WriteHeader(http.StatusOK)
_ = json.NewEncoder(w).Encode(updateResourceResponse{ _ = json.NewEncoder(w).Encode(updateResourceResponse{
ID: uint(resourceID), ID: uint(resourceID),
Key: req.Key, Key: req.Key,
@@ -264,57 +186,37 @@ func (h *aclAdminHandler) updateResource(w http.ResponseWriter, r *http.Request)
// @Produce json // @Produce json
// @Param resourceId path int true "Resource ID" example(1) // @Param resourceId path int true "Resource ID" example(1)
// @Success 200 // @Success 200
// @Failure 400 {object} deleteResourceErrorInvalidResourceID // @Failure 400 {object} ProblemDetails
// @Failure 404 {object} deleteResourceErrorResourceNotFound // @Failure 404 {object} ProblemDetails
// @Failure 409 {object} deleteResourceErrorResourceInUse // @Failure 409 {object} ProblemDetails
// @Failure 500 {object} errorInternalServerError // @Failure 500 {object} ProblemDetails
// @Router /api/acl/resources/{resourceId} [delete] // @Router /api/acl/resources/{resourceId} [delete]
func (h *aclAdminHandler) deleteResource(w http.ResponseWriter, r *http.Request) { func (h *aclAdminHandler) deleteResource(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
resourceIDStr := chi.URLParam(r, "resourceId") resourceIDStr := chi.URLParam(r, "resourceId")
resourceID, err := strconv.Atoi(resourceIDStr) resourceID, err := strconv.Atoi(resourceIDStr)
if err != nil || resourceID < 0 { if err != nil || resourceID < 0 {
w.WriteHeader(http.StatusBadRequest) writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-resource-id", "Invalid resource ID", "Resource ID must be positive integer", r)
_ = json.NewEncoder(w).Encode(deleteResourceErrorInvalidResourceID{
Error: ErrorInvalidResourceID,
Details: "Resource ID must be positive integer",
})
return return
} }
err = h.a.DeleteResource(uint(resourceID)) err = h.a.DeleteResource(uint(resourceID))
if err != nil { if err != nil {
slog.Error("Failed to delete resource", "error", err.Error()) slog.Error("Failed to delete resource", "error", err)
switch err { switch err {
case acl.ErrNotInitialized: case acl.ErrNotInitialized:
w.WriteHeader(http.StatusInternalServerError) writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "acl service is not initialized", r)
_ = json.NewEncoder(w).Encode(errorInternalServerError{
Error: ErrorInternalServerError,
Details: ErrorACLServiceNotInitialized,
})
return
case acl.ErrResourceNotFound: case acl.ErrResourceNotFound:
w.WriteHeader(http.StatusNotFound) writeProblem(w, http.StatusNotFound, "/errors/acl/resource-not-found", "Resource not found", "No resource with ID "+resourceIDStr, r)
_ = json.NewEncoder(w).Encode(deleteResourceErrorResourceNotFound{
Error: ErrorFailedToDeleteResource,
Details: "No resource with ID " + resourceIDStr,
})
return
case acl.ErrResourceInUse: case acl.ErrResourceInUse:
w.WriteHeader(http.StatusConflict) writeProblem(w, http.StatusConflict, "/errors/acl/resource-in-use", "Resource in use", "Resource "+resourceIDStr+" is in use", r)
_ = json.NewEncoder(w).Encode(deleteResourceErrorResourceInUse{
Error: ErrorFailedToDeleteResource,
Details: "Resource with ID " + resourceIDStr + " is used and cannot be deleted",
})
return
default: default:
w.WriteHeader(http.StatusInternalServerError) slog.Error("unexpected server error", "error", err.Error())
_ = json.NewEncoder(w).Encode(errorInternalServerError{ writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r)
Error: ErrorInternalServerError,
Details: "Failed to delete resource with ID '" + resourceIDStr + "'",
})
return
} }
return
} }
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)

View File

@@ -7,6 +7,8 @@ type getResourcesResponse []struct {
Key string `json:"key" example:"html.view"` Key string `json:"key" example:"html.view"`
} }
var _ getResourcesResponse // for documentation
/*******************************************************************/ /*******************************************************************/
// used in getResource() // used in getResource()
type getResourceResponse struct { type getResourceResponse struct {
@@ -14,16 +16,6 @@ type getResourceResponse struct {
Key string `json:"key" example:"html.view"` Key string `json:"key" example:"html.view"`
} }
type getResourceErrorInvalidResourceID struct {
Error string `json:"error" example:"INVALID_RESOURCE_ID"`
Details string `json:"details" example:"Resource ID must be positive integer"`
}
type getResourceErrorResourceNotFound struct {
Error string `json:"error" example:"RESOURCE_NOT_FOUND"`
Details string `json:"details" example:"No resource with ID 123"`
}
/*******************************************************************/ /*******************************************************************/
// used in createResource() // used in createResource()
type createResourceRequest struct { type createResourceRequest struct {
@@ -35,16 +27,6 @@ type createResourceResponse struct {
Key string `json:"key" example:"html.view"` Key string `json:"key" example:"html.view"`
} }
type createResourceErrorResourceAlreadyExists struct {
Error string `json:"error" example:"FAILED_TO_CREATE_RESOURCE"`
Details string `json:"details" example:"Resource with key 'html.view' already exists"`
}
type createResourceErrorInvalidResourceKey struct {
Error string `json:"error" example:"FAILED_TO_CREATE_RESOURCE"`
Details string `json:"details" example:"Invalid resource key"`
}
/*******************************************************************/ /*******************************************************************/
// used in updateResource() // used in updateResource()
type updateResourceRequest struct { type updateResourceRequest struct {
@@ -55,40 +37,3 @@ type updateResourceResponse struct {
ID uint `json:"id" example:"1"` ID uint `json:"id" example:"1"`
Key string `json:"key" example:"html.view"` Key string `json:"key" example:"html.view"`
} }
type updateResourceErrorResourceNotFound struct {
Error string `json:"error" example:"RESOURCE_NOT_FOUND"`
Details string `json:"details" example:"No resource with ID 123"`
}
type updateResourceErrorInvalidResourceID struct {
Error string `json:"error" example:"INVALID_RESOURCE_ID"`
Details string `json:"details" example:"Resource ID must be positive integer"`
}
type updateResourceErrorInvalidResourceKey struct {
Error string `json:"error" example:"FAILED_TO_UPDATE_RESOURCE"`
Details string `json:"details" example:"Invalid resource key"`
}
type updateResourceErrorResourceKeyAlreadyExists struct {
Error string `json:"error" example:"FAILED_TO_UPDATE_RESOURCE"`
Details string `json:"details" example:"Resource with key 'html.view' already exists"`
}
/*******************************************************************/
// used in deleteResource()
type deleteResourceErrorResourceNotFound struct {
Error string `json:"error" example:"RESOURCE_NOT_FOUND"`
Details string `json:"details" example:"No resource with ID 123"`
}
type deleteResourceErrorInvalidResourceID struct {
Error string `json:"error" example:"INVALID_RESOURCE_ID"`
Details string `json:"details" example:"Resource ID must be positive integer"`
}
type deleteResourceErrorResourceInUse struct {
Error string `json:"error" example:"FAILED_TO_DELETE_RESOURCE"`
Details string `json:"details" example:"Resource with ID 123 is used and cannot be deleted"`
}

View File

@@ -13,8 +13,8 @@ import (
// @Summary Get all roles // @Summary Get all roles
// @Tags roles // @Tags roles
// @Produce json // @Produce json
// @Success 200 {object} getRolesResponse // @Success 200 {array} getRolesResponse
// @Failure 500 {object} errorInternalServerError // @Failure 500 {object} ProblemDetails
// @Router /api/acl/roles [get] // @Router /api/acl/roles [get]
func (h *aclAdminHandler) getRoles(w http.ResponseWriter, r *http.Request) { func (h *aclAdminHandler) getRoles(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
@@ -22,35 +22,71 @@ func (h *aclAdminHandler) getRoles(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
switch err { switch err {
case acl.ErrNotInitialized: case acl.ErrNotInitialized:
w.WriteHeader(http.StatusInternalServerError) writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r)
_ = json.NewEncoder(w).Encode(errorInternalServerError{
Error: ErrorInternalServerError,
Details: ErrorACLServiceNotInitialized,
})
return
default: default:
w.WriteHeader(http.StatusInternalServerError) slog.Error("unexpected server error", "error", err.Error())
_ = json.NewEncoder(w).Encode(errorInternalServerError{ writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r)
Error: ErrorInternalServerError, }
Details: "Failed to get roles",
})
return return
} }
}
_ = json.NewEncoder(w).Encode(func() getRolesResponse { type R struct {
// Transform acl.Role to getRolesResponse
resp := make(getRolesResponse, 0, len(roles))
for _, role := range roles {
resp = append(resp, struct {
ID uint `json:"id" example:"1"` ID uint `json:"id" example:"1"`
Name string `json:"name" example:"admin"` Name string `json:"name" example:"admin"`
}{ }
ID: role.ID,
Name: role.Name, resp := make([]R, 0, len(roles))
for _, role := range roles {
resp = append(resp, R{ID: role.ID, Name: role.Name})
}
_ = json.NewEncoder(w).Encode(resp)
}
// @Summary Get role users
// @Tags roles
// @Produce json
// @Param roleId path int true "Role ID" example(1)
// @Success 200 {array} getRoleUsersResponse
// @Failure 400 {object} ProblemDetails
// @Failure 404 {object} ProblemDetails
// @Failure 500 {object} ProblemDetails
// @Router /api/acl/roles/{roleId}/users [get]
func (h *aclAdminHandler) getRoleUsers(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
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
}
role, err := h.a.GetRoleByID(uint(roleID))
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.ErrRoleNotFound:
writeProblem(w, http.StatusNotFound, "/errors/acl/role-not-found", "Role not found", "No role with ID "+roleIDStr, 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
}
if len(role.Users) == 0 {
writeProblem(w, http.StatusNotFound, "/errors/acl/role-has-no-users", "Role has no users", "Role has no users", r)
return
}
var respUsers getRoleUsersResponse
for _, user := range role.Users {
respUsers = append(respUsers, getRoleUser{
ID: user.ID,
Name: user.Username,
Email: user.Email,
}) })
} }
return resp _ = json.NewEncoder(w).Encode(respUsers)
}())
} }
// @Summary Get role by ID // @Summary Get role by ID
@@ -58,48 +94,33 @@ func (h *aclAdminHandler) getRoles(w http.ResponseWriter, r *http.Request) {
// @Produce json // @Produce json
// @Param roleId path int true "Role ID" example(1) // @Param roleId path int true "Role ID" example(1)
// @Success 200 {object} getRoleResponse // @Success 200 {object} getRoleResponse
// @Failure 400 {object} getRoleErrorInvalidRoleID // @Failure 400 {object} ProblemDetails
// @Failure 404 {object} getRoleErrorRoleNotFound // @Failure 404 {object} ProblemDetails
// @Failure 500 {object} errorInternalServerError // @Failure 500 {object} ProblemDetails
// @Router /api/acl/roles/{roleId} [get] // @Router /api/acl/roles/{roleId} [get]
func (h *aclAdminHandler) getRole(w http.ResponseWriter, r *http.Request) { func (h *aclAdminHandler) getRole(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
roleIDStr := chi.URLParam(r, "roleId") roleIDStr := chi.URLParam(r, "roleId")
roleID, err := strconv.Atoi(roleIDStr) roleID, err := strconv.Atoi(roleIDStr)
if err != nil || roleID < 0 { if err != nil || roleID < 0 {
w.WriteHeader(http.StatusBadRequest) writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-role-id", "Invalid role ID", "Role ID must be positive integer", r)
_ = json.NewEncoder(w).Encode(getRoleErrorInvalidRoleID{
Error: ErrorInvalidRoleID,
Details: "Role ID must be positive integer",
})
return return
} }
role, err := h.a.GetRoleByID(uint(roleID)) role, err := h.a.GetRoleByID(uint(roleID))
if err != nil { if err != nil {
switch err { switch err {
case acl.ErrNotInitialized: case acl.ErrNotInitialized:
w.WriteHeader(http.StatusInternalServerError) writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r)
_ = json.NewEncoder(w).Encode(errorInternalServerError{
Error: ErrorInternalServerError,
Details: ErrorACLServiceNotInitialized,
})
return
case acl.ErrRoleNotFound: case acl.ErrRoleNotFound:
w.WriteHeader(http.StatusNotFound) writeProblem(w, http.StatusNotFound, "/errors/acl/role-not-found", "Role not found", "No role with ID "+roleIDStr, r)
_ = json.NewEncoder(w).Encode(getRoleErrorRoleNotFound{
Error: ErrorRoleNotFound,
Details: "No role with ID " + roleIDStr,
})
return
default: default:
w.WriteHeader(http.StatusInternalServerError) slog.Error("unexpected server error", "error", err.Error())
_ = json.NewEncoder(w).Encode(errorInternalServerError{ writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r)
Error: ErrorInternalServerError, }
Details: "Failed to get role with ID " + roleIDStr,
})
return return
} }
}
_ = json.NewEncoder(w).Encode(getRoleResponse{ _ = json.NewEncoder(w).Encode(getRoleResponse{
ID: role.ID, ID: role.ID,
Name: role.Name, Name: role.Name,
@@ -112,56 +133,35 @@ func (h *aclAdminHandler) getRole(w http.ResponseWriter, r *http.Request) {
// @Produce json // @Produce json
// @Param request body createRoleRequest true "Role" // @Param request body createRoleRequest true "Role"
// @Success 201 {object} createRoleResponse // @Success 201 {object} createRoleResponse
// @Failure 400 {object} errorInvalidRequestBody // @Failure 400 {object} ProblemDetails
// @Failure 401 {object} createRoleErrorInvalidRoleName // @Failure 409 {object} ProblemDetails
// @Failure 409 {object} createRoleErrorRoleAlreadyExists // @Failure 500 {object} ProblemDetails
// @Failure 500 {object} errorInternalServerError
// @Router /api/acl/roles [post] // @Router /api/acl/roles [post]
func (h *aclAdminHandler) createRole(w http.ResponseWriter, r *http.Request) { func (h *aclAdminHandler) createRole(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
var req createRoleRequest var req createRoleRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil { if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
w.WriteHeader(http.StatusBadRequest) writeProblem(w, http.StatusBadRequest, "/errors/invalid-request-body", "Invalid request body", "Body is not valid JSON", r)
_ = json.NewEncoder(w).Encode(errorInvalidRequestBody{
Error: ErrorInvalidRequestBody,
Details: "Request body is not valid JSON",
})
return return
} }
roleID, err := h.a.CreateRole(req.Name) roleID, err := h.a.CreateRole(req.Name)
if err != nil { if err != nil {
slog.Error("Failed to create role", "error", err.Error()) slog.Error("Failed to create role", "error", err.Error())
switch err { switch err {
case acl.ErrNotInitialized: case acl.ErrNotInitialized:
w.WriteHeader(http.StatusInternalServerError) writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r)
_ = json.NewEncoder(w).Encode(errorInternalServerError{
Error: ErrorInternalServerError,
Details: ErrorACLServiceNotInitialized,
})
return
case acl.ErrRoleAlreadyExists:
w.WriteHeader(http.StatusConflict)
_ = json.NewEncoder(w).Encode(createRoleErrorRoleAlreadyExists{
Error: ErrorFailedToCreateRole,
Details: "Role with name '" + req.Name + "' already exists",
})
return
case acl.ErrInvalidRoleName: case acl.ErrInvalidRoleName:
w.WriteHeader(http.StatusBadRequest) writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-role-name", "Invalid role name", "Role name must be non-empty", r)
_ = json.NewEncoder(w).Encode(createRoleErrorInvalidRoleName{ case acl.ErrRoleAlreadyExists:
Error: ErrorFailedToCreateRole, writeProblem(w, http.StatusConflict, "/errors/acl/role-already-exists", "Role already exists", "Role '"+req.Name+"' already exists", r)
Details: "Role name must be non-empty string",
})
return
default: default:
w.WriteHeader(http.StatusInternalServerError) writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r)
_ = json.NewEncoder(w).Encode(errorInternalServerError{ }
Error: ErrorInternalServerError,
Details: "Failed to create role with name '" + req.Name + "'",
})
return return
} }
}
w.WriteHeader(http.StatusCreated) w.WriteHeader(http.StatusCreated)
_ = json.NewEncoder(w).Encode(createRoleResponse{ _ = json.NewEncoder(w).Encode(createRoleResponse{
ID: roleID, ID: roleID,
@@ -176,77 +176,45 @@ func (h *aclAdminHandler) createRole(w http.ResponseWriter, r *http.Request) {
// @Param roleId path int true "Role ID" example(1) // @Param roleId path int true "Role ID" example(1)
// @Param request body updateRoleRequest true "Role" // @Param request body updateRoleRequest true "Role"
// @Success 200 {object} updateRoleResponse // @Success 200 {object} updateRoleResponse
// @Failure 400 {object} errorInvalidRequestBody // @Failure 400 {object} ProblemDetails
// @Failure 400 {object} updateRoleErrorInvalidRoleID // @Failure 404 {object} ProblemDetails
// @Failure 400 {object} updateRoleErrorInvalidRoleName // @Failure 409 {object} ProblemDetails
// @Failure 404 {object} updateRoleErrorRoleNotFound // @Failure 500 {object} ProblemDetails
// @Failure 409 {object} updateRoleErrorRoleNameAlreadyExists
// @Failure 500 {object} errorInternalServerError
// @Router /api/acl/roles/{roleId} [patch] // @Router /api/acl/roles/{roleId} [patch]
func (h *aclAdminHandler) updateRole(w http.ResponseWriter, r *http.Request) { func (h *aclAdminHandler) updateRole(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
var req updateRoleRequest var req updateRoleRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil { if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
w.WriteHeader(http.StatusBadRequest) writeProblem(w, http.StatusBadRequest, "/errors/invalid-request-body", "Invalid request body", "Body is not valid JSON", r)
_ = json.NewEncoder(w).Encode(errorInvalidRequestBody{
Error: ErrorInvalidRequestBody,
Details: "Request body is not valid JSON",
})
return return
} }
roleIDStr := chi.URLParam(r, "roleId") roleIDStr := chi.URLParam(r, "roleId")
roleID, err := strconv.Atoi(roleIDStr) roleID, err := strconv.Atoi(roleIDStr)
if err != nil || roleID < 0 { if err != nil || roleID < 0 {
w.WriteHeader(http.StatusBadRequest) writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-role-id", "Invalid role ID", "Role ID must be positive integer", r)
_ = json.NewEncoder(w).Encode(updateRoleErrorInvalidRoleID{
Error: ErrorInvalidRoleID,
Details: "Role ID must be positive integer",
})
return return
} }
err = h.a.UpdateRole(uint(roleID), req.Name) err = h.a.UpdateRole(uint(roleID), req.Name)
// TODO: make error handling more specific in acl service
if err != nil { if err != nil {
slog.Error("Failed to update role", "error", err.Error()) slog.Error("Failed to update role", "error", err.Error())
switch err { switch err {
case acl.ErrNotInitialized: case acl.ErrNotInitialized:
w.WriteHeader(http.StatusInternalServerError) writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r)
_ = json.NewEncoder(w).Encode(errorInternalServerError{
Error: ErrorInternalServerError,
Details: ErrorACLServiceNotInitialized,
})
return
case acl.ErrInvalidRoleName: case acl.ErrInvalidRoleName:
w.WriteHeader(http.StatusBadRequest) writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-role-name", "Invalid role name", "Role name must be non-empty", r)
_ = json.NewEncoder(w).Encode(updateRoleErrorInvalidRoleName{
Error: ErrorFailedToUpdateRole,
Details: "Invalid role name",
})
return
case acl.ErrRoleNotFound: case acl.ErrRoleNotFound:
w.WriteHeader(http.StatusNotFound) writeProblem(w, http.StatusNotFound, "/errors/acl/role-not-found", "Role not found", "No role with ID "+roleIDStr, r)
_ = json.NewEncoder(w).Encode(updateRoleErrorRoleNotFound{
Error: ErrorFailedToUpdateRole,
Details: "No role with ID " + roleIDStr,
})
return
case acl.ErrSameRoleName: case acl.ErrSameRoleName:
w.WriteHeader(http.StatusConflict) writeProblem(w, http.StatusConflict, "/errors/acl/role-name-already-exists", "Role name already exists", "Role '"+req.Name+"' already exists", r)
_ = json.NewEncoder(w).Encode(updateRoleErrorRoleNameAlreadyExists{
Error: ErrorFailedToUpdateRole,
Details: "Role with name '" + req.Name + "' already exists",
})
return
default: default:
w.WriteHeader(http.StatusInternalServerError) writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r)
_ = json.NewEncoder(w).Encode(errorInternalServerError{ }
Error: ErrorInternalServerError,
Details: "Failed to update role with name '" + req.Name + "'",
})
return return
} }
}
w.WriteHeader(http.StatusOK)
_ = json.NewEncoder(w).Encode(updateRoleResponse{ _ = json.NewEncoder(w).Encode(updateRoleResponse{
ID: uint(roleID), ID: uint(roleID),
Name: req.Name, Name: req.Name,
@@ -258,57 +226,35 @@ func (h *aclAdminHandler) updateRole(w http.ResponseWriter, r *http.Request) {
// @Produce json // @Produce json
// @Param roleId path int true "Role ID" example(1) // @Param roleId path int true "Role ID" example(1)
// @Success 200 // @Success 200
// @Failure 400 {object} deleteRoleErrorInvalidRoleID // @Failure 400 {object} ProblemDetails
// @Failure 404 {object} deleteRoleErrorRoleNotFound // @Failure 404 {object} ProblemDetails
// @Failure 409 {object} deleteRoleErrorRoleInUse // @Failure 409 {object} ProblemDetails
// @Failure 500 {object} errorInternalServerError // @Failure 500 {object} ProblemDetails
// @Router /api/acl/roles/{roleId} [delete] // @Router /api/acl/roles/{roleId} [delete]
func (h *aclAdminHandler) deleteRole(w http.ResponseWriter, r *http.Request) { func (h *aclAdminHandler) deleteRole(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
roleIDStr := chi.URLParam(r, "roleId") roleIDStr := chi.URLParam(r, "roleId")
roleID, err := strconv.Atoi(roleIDStr) roleID, err := strconv.Atoi(roleIDStr)
if err != nil || roleID < 0 { if err != nil || roleID < 0 {
w.WriteHeader(http.StatusBadRequest) writeProblem(w, http.StatusBadRequest, "/errors/acl/invalid-role-id", "Invalid role ID", "Role ID must be positive integer", r)
_ = json.NewEncoder(w).Encode(deleteRoleErrorInvalidRoleID{
Error: ErrorInvalidRoleID,
Details: "Role ID must be positive integer",
})
return return
} }
err = h.a.DeleteRole(uint(roleID)) err = h.a.DeleteRole(uint(roleID))
// TODO: make error handling more specific in acl service
if err != nil { if err != nil {
slog.Error("Failed to delete role", "error", err.Error()) slog.Error("Failed to delete role", "error", err.Error())
switch err { switch err {
case acl.ErrNotInitialized: case acl.ErrNotInitialized:
w.WriteHeader(http.StatusInternalServerError) writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "ACL service is not initialized", r)
_ = json.NewEncoder(w).Encode(errorInternalServerError{
Error: ErrorInternalServerError,
Details: ErrorACLServiceNotInitialized,
})
return
case acl.ErrRoleNotFound: case acl.ErrRoleNotFound:
w.WriteHeader(http.StatusNotFound) writeProblem(w, http.StatusNotFound, "/errors/acl/role-not-found", "Role not found", "No role with ID "+roleIDStr, r)
_ = json.NewEncoder(w).Encode(deleteRoleErrorRoleNotFound{
Error: ErrorFailedToDeleteRole,
Details: "No role with ID " + roleIDStr,
})
return
case acl.ErrRoleInUse: case acl.ErrRoleInUse:
w.WriteHeader(http.StatusConflict) writeProblem(w, http.StatusConflict, "/errors/acl/role-in-use", "Role in use", "Role "+roleIDStr+" is assigned to at least one user and cannot be deleted", r)
_ = json.NewEncoder(w).Encode(deleteRoleErrorRoleInUse{
Error: ErrorFailedToDeleteRole,
Details: "Role with ID " + roleIDStr + " is assigned to users and cannot be deleted",
})
return
default: default:
w.WriteHeader(http.StatusInternalServerError) writeProblem(w, http.StatusInternalServerError, "/errors/internal-server-error", "Internal Server Error", "unexpected error", r)
_ = json.NewEncoder(w).Encode(errorInternalServerError{ }
Error: ErrorInternalServerError,
Details: "Failed to delete role with ID '" + roleIDStr + "'",
})
return return
} }
}
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
} }

View File

@@ -7,6 +7,8 @@ type getRolesResponse []struct {
Name string `json:"name" example:"admin"` Name string `json:"name" example:"admin"`
} }
var _ getRolesResponse
/*******************************************************************/ /*******************************************************************/
// used in getRole() // used in getRole()
type getRoleResponse struct { type getRoleResponse struct {
@@ -14,15 +16,14 @@ type getRoleResponse struct {
Name string `json:"name" example:"admin"` Name string `json:"name" example:"admin"`
} }
type getRoleErrorInvalidRoleID struct { /*******************************************************************/
Error string `json:"error" example:"INVALID_ROLE_ID"` // used in getRoleUsers()
Details string `json:"details" example:"Role ID must be positive integer"` type getRoleUser struct {
} ID uint `json:"id" example:"1"`
Name string `json:"username" example:"admin"`
type getRoleErrorRoleNotFound struct { Email string `json:"email" example:"admin@triggerssmith.com"`
Error string `json:"error" example:"ROLE_NOT_FOUND"`
Details string `json:"details" example:"No role with ID 123"`
} }
type getRoleUsersResponse []getRoleUser
/*******************************************************************/ /*******************************************************************/
// used in createRole() // used in createRole()
@@ -35,16 +36,6 @@ type createRoleResponse struct {
Name string `json:"name" example:"admin"` Name string `json:"name" example:"admin"`
} }
type createRoleErrorRoleAlreadyExists struct {
Error string `json:"error" example:"FAILED_TO_CREATE_ROLE"`
Details string `json:"details" example:"Role with name 'admin' already exists"`
}
type createRoleErrorInvalidRoleName struct {
Error string `json:"error" example:"FAILED_TO_CREATE_ROLE"`
Details string `json:"details" example:"Invalid role name"`
}
/*******************************************************************/ /*******************************************************************/
// used in updateRole() // used in updateRole()
type updateRoleRequest struct { type updateRoleRequest struct {
@@ -55,40 +46,3 @@ type updateRoleResponse struct {
ID uint `json:"id" example:"1"` ID uint `json:"id" example:"1"`
Name string `json:"name" example:"admin"` Name string `json:"name" example:"admin"`
} }
type updateRoleErrorRoleNotFound struct {
Error string `json:"error" example:"ROLE_NOT_FOUND"`
Details string `json:"details" example:"No role with ID 123"`
}
type updateRoleErrorInvalidRoleID struct {
Error string `json:"error" example:"INVALID_ROLE_ID"`
Details string `json:"details" example:"Role ID must be positive integer"`
}
type updateRoleErrorInvalidRoleName struct {
Error string `json:"error" example:"FAILED_TO_UPDATE_ROLE"`
Details string `json:"details" example:"Invalid role name"`
}
type updateRoleErrorRoleNameAlreadyExists struct {
Error string `json:"error" example:"FAILED_TO_UPDATE_ROLE"`
Details string `json:"details" example:"Role with name 'admin' already exists"`
}
/*******************************************************************/
// used in deleteRole()
type deleteRoleErrorRoleNotFound struct {
Error string `json:"error" example:"ROLE_NOT_FOUND"`
Details string `json:"details" example:"No role with ID 123"`
}
type deleteRoleErrorInvalidRoleID struct {
Error string `json:"error" example:"INVALID_ROLE_ID"`
Details string `json:"details" example:"Role ID must be positive integer"`
}
type deleteRoleErrorRoleInUse struct {
Error string `json:"error" example:"FAILED_TO_DELETE_ROLE"`
Details string `json:"details" example:"Role with ID 123 is assigned to users and cannot be deleted"`
}

View File

@@ -5,6 +5,7 @@ package api_auth
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"log/slog"
"net/http" "net/http"
"time" "time"
@@ -79,6 +80,7 @@ func (h *authHandler) handleRegister(w http.ResponseWriter, r *http.Request) {
user, err := h.a.Register(req.Username, req.Email, req.Password) user, err := h.a.Register(req.Username, req.Email, req.Password)
if err != nil { if err != nil {
slog.Error("Failed to register user", "error", err)
http.Error(w, "Registration failed", http.StatusInternalServerError) http.Error(w, "Registration failed", http.StatusInternalServerError)
return return
} }

View File

@@ -90,7 +90,7 @@ func (r *Router) MustRoute() chi.Router {
api.Route("/block", api_block.MustRoute(r.cfg)) api.Route("/block", api_block.MustRoute(r.cfg))
authRoute := api_auth.MustRoute(r.cfg, r.authService) authRoute := api_auth.MustRoute(r.cfg, r.authService)
api.Route("/auth", authRoute) api.Route("/auth", authRoute)
api.Route("/users", authRoute) // legacy support //api.Route("/users", authRoute) // legacy support
aclAdminRoute := api_acladmin.MustRoute(r.cfg, r.aclService, r.authService) aclAdminRoute := api_acladmin.MustRoute(r.cfg, r.aclService, r.authService)
api.Route("/acl", aclAdminRoute) api.Route("/acl", aclAdminRoute)
api.Route("/acl-admin", aclAdminRoute) // legacy support api.Route("/acl-admin", aclAdminRoute) // legacy support

View File

@@ -28,6 +28,8 @@ const docTemplate = `{
"200": { "200": {
"description": "OK", "description": "OK",
"schema": { "schema": {
"type": "array",
"items": {
"type": "array", "type": "array",
"items": { "items": {
"type": "object", "type": "object",
@@ -43,11 +45,12 @@ const docTemplate = `{
} }
} }
} }
}
}, },
"500": { "500": {
"description": "Internal Server Error", "description": "Internal Server Error",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.errorInternalServerError" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
} }
} }
@@ -84,19 +87,19 @@ const docTemplate = `{
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.createResourceErrorInvalidResourceKey" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"409": { "409": {
"description": "Conflict", "description": "Conflict",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.createResourceErrorResourceAlreadyExists" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"500": { "500": {
"description": "Internal Server Error", "description": "Internal Server Error",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.errorInternalServerError" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
} }
} }
@@ -131,19 +134,19 @@ const docTemplate = `{
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.getResourceErrorInvalidResourceID" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.getResourceErrorResourceNotFound" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"500": { "500": {
"description": "Internal Server Error", "description": "Internal Server Error",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.errorInternalServerError" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
} }
} }
@@ -173,25 +176,25 @@ const docTemplate = `{
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.deleteResourceErrorInvalidResourceID" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.deleteResourceErrorResourceNotFound" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"409": { "409": {
"description": "Conflict", "description": "Conflict",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.deleteResourceErrorResourceInUse" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"500": { "500": {
"description": "Internal Server Error", "description": "Internal Server Error",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.errorInternalServerError" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
} }
} }
@@ -236,25 +239,25 @@ const docTemplate = `{
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.updateResourceErrorInvalidResourceKey" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.updateResourceErrorResourceNotFound" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"409": { "409": {
"description": "Conflict", "description": "Conflict",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.updateResourceErrorResourceKeyAlreadyExists" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"500": { "500": {
"description": "Internal Server Error", "description": "Internal Server Error",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.errorInternalServerError" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
} }
} }
@@ -273,6 +276,8 @@ const docTemplate = `{
"200": { "200": {
"description": "OK", "description": "OK",
"schema": { "schema": {
"type": "array",
"items": {
"type": "array", "type": "array",
"items": { "items": {
"type": "object", "type": "object",
@@ -288,11 +293,12 @@ const docTemplate = `{
} }
} }
} }
}
}, },
"500": { "500": {
"description": "Internal Server Error", "description": "Internal Server Error",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.errorInternalServerError" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
} }
} }
@@ -329,25 +335,19 @@ const docTemplate = `{
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.errorInvalidRequestBody" "$ref": "#/definitions/api_acladmin.ProblemDetails"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/api_acladmin.createRoleErrorInvalidRoleName"
} }
}, },
"409": { "409": {
"description": "Conflict", "description": "Conflict",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.createRoleErrorRoleAlreadyExists" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"500": { "500": {
"description": "Internal Server Error", "description": "Internal Server Error",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.errorInternalServerError" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
} }
} }
@@ -382,19 +382,19 @@ const docTemplate = `{
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.getRoleErrorInvalidRoleID" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.getRoleErrorRoleNotFound" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"500": { "500": {
"description": "Internal Server Error", "description": "Internal Server Error",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.errorInternalServerError" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
} }
} }
@@ -424,25 +424,25 @@ const docTemplate = `{
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.deleteRoleErrorInvalidRoleID" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.deleteRoleErrorRoleNotFound" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"409": { "409": {
"description": "Conflict", "description": "Conflict",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.deleteRoleErrorRoleInUse" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"500": { "500": {
"description": "Internal Server Error", "description": "Internal Server Error",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.errorInternalServerError" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
} }
} }
@@ -487,25 +487,78 @@ const docTemplate = `{
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.updateRoleErrorInvalidRoleName" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.updateRoleErrorRoleNotFound" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"409": { "409": {
"description": "Conflict", "description": "Conflict",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.updateRoleErrorRoleNameAlreadyExists" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"500": { "500": {
"description": "Internal Server Error", "description": "Internal Server Error",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.errorInternalServerError" "$ref": "#/definitions/api_acladmin.ProblemDetails"
}
}
}
}
},
"/api/acl/roles/{roleId}/users": {
"get": {
"produces": [
"application/json"
],
"tags": [
"roles"
],
"summary": "Get role users",
"parameters": [
{
"type": "integer",
"example": 1,
"description": "Role ID",
"name": "roleId",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/api_acladmin.getRoleUser"
}
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/api_acladmin.ProblemDetails"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/api_acladmin.ProblemDetails"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
} }
} }
@@ -513,29 +566,28 @@ const docTemplate = `{
} }
}, },
"definitions": { "definitions": {
"api_acladmin.createResourceErrorInvalidResourceKey": { "api_acladmin.ProblemDetails": {
"type": "object", "type": "object",
"properties": { "properties": {
"details": { "detail": {
"type": "string", "type": "string",
"example": "Invalid resource key" "example": "No role with ID 42"
}, },
"error": { "instance": {
"type": "string", "type": "string",
"example": "FAILED_TO_CREATE_RESOURCE" "example": "/api/acl/roles/42"
}
}
}, },
"api_acladmin.createResourceErrorResourceAlreadyExists": { "status": {
"type": "object", "type": "integer",
"properties": { "example": 404
"details": {
"type": "string",
"example": "Resource with key 'html.view' already exists"
}, },
"error": { "title": {
"type": "string", "type": "string",
"example": "FAILED_TO_CREATE_RESOURCE" "example": "Role not found"
},
"type": {
"type": "string",
"example": "https://api.triggerssmith.com/errors/role-not-found"
} }
} }
}, },
@@ -561,32 +613,6 @@ const docTemplate = `{
} }
} }
}, },
"api_acladmin.createRoleErrorInvalidRoleName": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "Invalid role name"
},
"error": {
"type": "string",
"example": "FAILED_TO_CREATE_ROLE"
}
}
},
"api_acladmin.createRoleErrorRoleAlreadyExists": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "Role with name 'admin' already exists"
},
"error": {
"type": "string",
"example": "FAILED_TO_CREATE_ROLE"
}
}
},
"api_acladmin.createRoleRequest": { "api_acladmin.createRoleRequest": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -609,134 +635,6 @@ const docTemplate = `{
} }
} }
}, },
"api_acladmin.deleteResourceErrorInvalidResourceID": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "Resource ID must be positive integer"
},
"error": {
"type": "string",
"example": "INVALID_RESOURCE_ID"
}
}
},
"api_acladmin.deleteResourceErrorResourceInUse": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "Resource with ID 123 is used and cannot be deleted"
},
"error": {
"type": "string",
"example": "FAILED_TO_DELETE_RESOURCE"
}
}
},
"api_acladmin.deleteResourceErrorResourceNotFound": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "No resource with ID 123"
},
"error": {
"type": "string",
"example": "RESOURCE_NOT_FOUND"
}
}
},
"api_acladmin.deleteRoleErrorInvalidRoleID": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "Role ID must be positive integer"
},
"error": {
"type": "string",
"example": "INVALID_ROLE_ID"
}
}
},
"api_acladmin.deleteRoleErrorRoleInUse": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "Role with ID 123 is assigned to users and cannot be deleted"
},
"error": {
"type": "string",
"example": "FAILED_TO_DELETE_ROLE"
}
}
},
"api_acladmin.deleteRoleErrorRoleNotFound": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "No role with ID 123"
},
"error": {
"type": "string",
"example": "ROLE_NOT_FOUND"
}
}
},
"api_acladmin.errorInternalServerError": {
"type": "object",
"properties": {
"details": {
"type": "string"
},
"error": {
"type": "string"
}
}
},
"api_acladmin.errorInvalidRequestBody": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "Request body is not valid JSON"
},
"error": {
"type": "string",
"example": "INVALID_REQUEST_BODY"
}
}
},
"api_acladmin.getResourceErrorInvalidResourceID": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "Resource ID must be positive integer"
},
"error": {
"type": "string",
"example": "INVALID_RESOURCE_ID"
}
}
},
"api_acladmin.getResourceErrorResourceNotFound": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "No resource with ID 123"
},
"error": {
"type": "string",
"example": "RESOURCE_NOT_FOUND"
}
}
},
"api_acladmin.getResourceResponse": { "api_acladmin.getResourceResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -750,32 +648,6 @@ const docTemplate = `{
} }
} }
}, },
"api_acladmin.getRoleErrorInvalidRoleID": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "Role ID must be positive integer"
},
"error": {
"type": "string",
"example": "INVALID_ROLE_ID"
}
}
},
"api_acladmin.getRoleErrorRoleNotFound": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "No role with ID 123"
},
"error": {
"type": "string",
"example": "ROLE_NOT_FOUND"
}
}
},
"api_acladmin.getRoleResponse": { "api_acladmin.getRoleResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -789,55 +661,20 @@ const docTemplate = `{
} }
} }
}, },
"api_acladmin.updateResourceErrorInvalidResourceID": { "api_acladmin.getRoleUser": {
"type": "object", "type": "object",
"properties": { "properties": {
"details": { "userEmail": {
"type": "string", "type": "string",
"example": "Resource ID must be positive integer" "example": "admin@triggerssmith.com"
}, },
"error": { "userId": {
"type": "string", "type": "integer",
"example": "INVALID_RESOURCE_ID" "example": 1
}
}
}, },
"api_acladmin.updateResourceErrorInvalidResourceKey": { "userName": {
"type": "object",
"properties": {
"details": {
"type": "string", "type": "string",
"example": "Invalid resource key" "example": "admin"
},
"error": {
"type": "string",
"example": "FAILED_TO_UPDATE_RESOURCE"
}
}
},
"api_acladmin.updateResourceErrorResourceKeyAlreadyExists": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "Resource with key 'html.view' already exists"
},
"error": {
"type": "string",
"example": "FAILED_TO_UPDATE_RESOURCE"
}
}
},
"api_acladmin.updateResourceErrorResourceNotFound": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "No resource with ID 123"
},
"error": {
"type": "string",
"example": "RESOURCE_NOT_FOUND"
} }
} }
}, },
@@ -863,58 +700,6 @@ const docTemplate = `{
} }
} }
}, },
"api_acladmin.updateRoleErrorInvalidRoleID": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "Role ID must be positive integer"
},
"error": {
"type": "string",
"example": "INVALID_ROLE_ID"
}
}
},
"api_acladmin.updateRoleErrorInvalidRoleName": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "Invalid role name"
},
"error": {
"type": "string",
"example": "FAILED_TO_UPDATE_ROLE"
}
}
},
"api_acladmin.updateRoleErrorRoleNameAlreadyExists": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "Role with name 'admin' already exists"
},
"error": {
"type": "string",
"example": "FAILED_TO_UPDATE_ROLE"
}
}
},
"api_acladmin.updateRoleErrorRoleNotFound": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "No role with ID 123"
},
"error": {
"type": "string",
"example": "ROLE_NOT_FOUND"
}
}
},
"api_acladmin.updateRoleRequest": { "api_acladmin.updateRoleRequest": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@@ -17,6 +17,8 @@
"200": { "200": {
"description": "OK", "description": "OK",
"schema": { "schema": {
"type": "array",
"items": {
"type": "array", "type": "array",
"items": { "items": {
"type": "object", "type": "object",
@@ -32,11 +34,12 @@
} }
} }
} }
}
}, },
"500": { "500": {
"description": "Internal Server Error", "description": "Internal Server Error",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.errorInternalServerError" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
} }
} }
@@ -73,19 +76,19 @@
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.createResourceErrorInvalidResourceKey" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"409": { "409": {
"description": "Conflict", "description": "Conflict",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.createResourceErrorResourceAlreadyExists" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"500": { "500": {
"description": "Internal Server Error", "description": "Internal Server Error",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.errorInternalServerError" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
} }
} }
@@ -120,19 +123,19 @@
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.getResourceErrorInvalidResourceID" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.getResourceErrorResourceNotFound" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"500": { "500": {
"description": "Internal Server Error", "description": "Internal Server Error",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.errorInternalServerError" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
} }
} }
@@ -162,25 +165,25 @@
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.deleteResourceErrorInvalidResourceID" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.deleteResourceErrorResourceNotFound" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"409": { "409": {
"description": "Conflict", "description": "Conflict",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.deleteResourceErrorResourceInUse" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"500": { "500": {
"description": "Internal Server Error", "description": "Internal Server Error",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.errorInternalServerError" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
} }
} }
@@ -225,25 +228,25 @@
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.updateResourceErrorInvalidResourceKey" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.updateResourceErrorResourceNotFound" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"409": { "409": {
"description": "Conflict", "description": "Conflict",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.updateResourceErrorResourceKeyAlreadyExists" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"500": { "500": {
"description": "Internal Server Error", "description": "Internal Server Error",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.errorInternalServerError" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
} }
} }
@@ -262,6 +265,8 @@
"200": { "200": {
"description": "OK", "description": "OK",
"schema": { "schema": {
"type": "array",
"items": {
"type": "array", "type": "array",
"items": { "items": {
"type": "object", "type": "object",
@@ -277,11 +282,12 @@
} }
} }
} }
}
}, },
"500": { "500": {
"description": "Internal Server Error", "description": "Internal Server Error",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.errorInternalServerError" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
} }
} }
@@ -318,25 +324,19 @@
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.errorInvalidRequestBody" "$ref": "#/definitions/api_acladmin.ProblemDetails"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/api_acladmin.createRoleErrorInvalidRoleName"
} }
}, },
"409": { "409": {
"description": "Conflict", "description": "Conflict",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.createRoleErrorRoleAlreadyExists" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"500": { "500": {
"description": "Internal Server Error", "description": "Internal Server Error",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.errorInternalServerError" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
} }
} }
@@ -371,19 +371,19 @@
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.getRoleErrorInvalidRoleID" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.getRoleErrorRoleNotFound" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"500": { "500": {
"description": "Internal Server Error", "description": "Internal Server Error",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.errorInternalServerError" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
} }
} }
@@ -413,25 +413,25 @@
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.deleteRoleErrorInvalidRoleID" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.deleteRoleErrorRoleNotFound" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"409": { "409": {
"description": "Conflict", "description": "Conflict",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.deleteRoleErrorRoleInUse" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"500": { "500": {
"description": "Internal Server Error", "description": "Internal Server Error",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.errorInternalServerError" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
} }
} }
@@ -476,25 +476,78 @@
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.updateRoleErrorInvalidRoleName" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.updateRoleErrorRoleNotFound" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"409": { "409": {
"description": "Conflict", "description": "Conflict",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.updateRoleErrorRoleNameAlreadyExists" "$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
}, },
"500": { "500": {
"description": "Internal Server Error", "description": "Internal Server Error",
"schema": { "schema": {
"$ref": "#/definitions/api_acladmin.errorInternalServerError" "$ref": "#/definitions/api_acladmin.ProblemDetails"
}
}
}
}
},
"/api/acl/roles/{roleId}/users": {
"get": {
"produces": [
"application/json"
],
"tags": [
"roles"
],
"summary": "Get role users",
"parameters": [
{
"type": "integer",
"example": 1,
"description": "Role ID",
"name": "roleId",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/api_acladmin.getRoleUser"
}
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/api_acladmin.ProblemDetails"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/api_acladmin.ProblemDetails"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/api_acladmin.ProblemDetails"
} }
} }
} }
@@ -502,29 +555,28 @@
} }
}, },
"definitions": { "definitions": {
"api_acladmin.createResourceErrorInvalidResourceKey": { "api_acladmin.ProblemDetails": {
"type": "object", "type": "object",
"properties": { "properties": {
"details": { "detail": {
"type": "string", "type": "string",
"example": "Invalid resource key" "example": "No role with ID 42"
}, },
"error": { "instance": {
"type": "string", "type": "string",
"example": "FAILED_TO_CREATE_RESOURCE" "example": "/api/acl/roles/42"
}
}
}, },
"api_acladmin.createResourceErrorResourceAlreadyExists": { "status": {
"type": "object", "type": "integer",
"properties": { "example": 404
"details": {
"type": "string",
"example": "Resource with key 'html.view' already exists"
}, },
"error": { "title": {
"type": "string", "type": "string",
"example": "FAILED_TO_CREATE_RESOURCE" "example": "Role not found"
},
"type": {
"type": "string",
"example": "https://api.triggerssmith.com/errors/role-not-found"
} }
} }
}, },
@@ -550,32 +602,6 @@
} }
} }
}, },
"api_acladmin.createRoleErrorInvalidRoleName": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "Invalid role name"
},
"error": {
"type": "string",
"example": "FAILED_TO_CREATE_ROLE"
}
}
},
"api_acladmin.createRoleErrorRoleAlreadyExists": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "Role with name 'admin' already exists"
},
"error": {
"type": "string",
"example": "FAILED_TO_CREATE_ROLE"
}
}
},
"api_acladmin.createRoleRequest": { "api_acladmin.createRoleRequest": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -598,134 +624,6 @@
} }
} }
}, },
"api_acladmin.deleteResourceErrorInvalidResourceID": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "Resource ID must be positive integer"
},
"error": {
"type": "string",
"example": "INVALID_RESOURCE_ID"
}
}
},
"api_acladmin.deleteResourceErrorResourceInUse": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "Resource with ID 123 is used and cannot be deleted"
},
"error": {
"type": "string",
"example": "FAILED_TO_DELETE_RESOURCE"
}
}
},
"api_acladmin.deleteResourceErrorResourceNotFound": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "No resource with ID 123"
},
"error": {
"type": "string",
"example": "RESOURCE_NOT_FOUND"
}
}
},
"api_acladmin.deleteRoleErrorInvalidRoleID": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "Role ID must be positive integer"
},
"error": {
"type": "string",
"example": "INVALID_ROLE_ID"
}
}
},
"api_acladmin.deleteRoleErrorRoleInUse": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "Role with ID 123 is assigned to users and cannot be deleted"
},
"error": {
"type": "string",
"example": "FAILED_TO_DELETE_ROLE"
}
}
},
"api_acladmin.deleteRoleErrorRoleNotFound": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "No role with ID 123"
},
"error": {
"type": "string",
"example": "ROLE_NOT_FOUND"
}
}
},
"api_acladmin.errorInternalServerError": {
"type": "object",
"properties": {
"details": {
"type": "string"
},
"error": {
"type": "string"
}
}
},
"api_acladmin.errorInvalidRequestBody": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "Request body is not valid JSON"
},
"error": {
"type": "string",
"example": "INVALID_REQUEST_BODY"
}
}
},
"api_acladmin.getResourceErrorInvalidResourceID": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "Resource ID must be positive integer"
},
"error": {
"type": "string",
"example": "INVALID_RESOURCE_ID"
}
}
},
"api_acladmin.getResourceErrorResourceNotFound": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "No resource with ID 123"
},
"error": {
"type": "string",
"example": "RESOURCE_NOT_FOUND"
}
}
},
"api_acladmin.getResourceResponse": { "api_acladmin.getResourceResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -739,32 +637,6 @@
} }
} }
}, },
"api_acladmin.getRoleErrorInvalidRoleID": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "Role ID must be positive integer"
},
"error": {
"type": "string",
"example": "INVALID_ROLE_ID"
}
}
},
"api_acladmin.getRoleErrorRoleNotFound": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "No role with ID 123"
},
"error": {
"type": "string",
"example": "ROLE_NOT_FOUND"
}
}
},
"api_acladmin.getRoleResponse": { "api_acladmin.getRoleResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -778,55 +650,20 @@
} }
} }
}, },
"api_acladmin.updateResourceErrorInvalidResourceID": { "api_acladmin.getRoleUser": {
"type": "object", "type": "object",
"properties": { "properties": {
"details": { "userEmail": {
"type": "string", "type": "string",
"example": "Resource ID must be positive integer" "example": "admin@triggerssmith.com"
}, },
"error": { "userId": {
"type": "string", "type": "integer",
"example": "INVALID_RESOURCE_ID" "example": 1
}
}
}, },
"api_acladmin.updateResourceErrorInvalidResourceKey": { "userName": {
"type": "object",
"properties": {
"details": {
"type": "string", "type": "string",
"example": "Invalid resource key" "example": "admin"
},
"error": {
"type": "string",
"example": "FAILED_TO_UPDATE_RESOURCE"
}
}
},
"api_acladmin.updateResourceErrorResourceKeyAlreadyExists": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "Resource with key 'html.view' already exists"
},
"error": {
"type": "string",
"example": "FAILED_TO_UPDATE_RESOURCE"
}
}
},
"api_acladmin.updateResourceErrorResourceNotFound": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "No resource with ID 123"
},
"error": {
"type": "string",
"example": "RESOURCE_NOT_FOUND"
} }
} }
}, },
@@ -852,58 +689,6 @@
} }
} }
}, },
"api_acladmin.updateRoleErrorInvalidRoleID": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "Role ID must be positive integer"
},
"error": {
"type": "string",
"example": "INVALID_ROLE_ID"
}
}
},
"api_acladmin.updateRoleErrorInvalidRoleName": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "Invalid role name"
},
"error": {
"type": "string",
"example": "FAILED_TO_UPDATE_ROLE"
}
}
},
"api_acladmin.updateRoleErrorRoleNameAlreadyExists": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "Role with name 'admin' already exists"
},
"error": {
"type": "string",
"example": "FAILED_TO_UPDATE_ROLE"
}
}
},
"api_acladmin.updateRoleErrorRoleNotFound": {
"type": "object",
"properties": {
"details": {
"type": "string",
"example": "No role with ID 123"
},
"error": {
"type": "string",
"example": "ROLE_NOT_FOUND"
}
}
},
"api_acladmin.updateRoleRequest": { "api_acladmin.updateRoleRequest": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@@ -1,20 +1,20 @@
definitions: definitions:
api_acladmin.createResourceErrorInvalidResourceKey: api_acladmin.ProblemDetails:
properties: properties:
details: detail:
example: Invalid resource key example: No role with ID 42
type: string type: string
error: instance:
example: FAILED_TO_CREATE_RESOURCE example: /api/acl/roles/42
type: string type: string
type: object status:
api_acladmin.createResourceErrorResourceAlreadyExists: example: 404
properties: type: integer
details: title:
example: Resource with key 'html.view' already exists example: Role not found
type: string type: string
error: type:
example: FAILED_TO_CREATE_RESOURCE example: https://api.triggerssmith.com/errors/role-not-found
type: string type: string
type: object type: object
api_acladmin.createResourceRequest: api_acladmin.createResourceRequest:
@@ -32,24 +32,6 @@ definitions:
example: html.view example: html.view
type: string type: string
type: object type: object
api_acladmin.createRoleErrorInvalidRoleName:
properties:
details:
example: Invalid role name
type: string
error:
example: FAILED_TO_CREATE_ROLE
type: string
type: object
api_acladmin.createRoleErrorRoleAlreadyExists:
properties:
details:
example: Role with name 'admin' already exists
type: string
error:
example: FAILED_TO_CREATE_ROLE
type: string
type: object
api_acladmin.createRoleRequest: api_acladmin.createRoleRequest:
properties: properties:
name: name:
@@ -65,94 +47,6 @@ definitions:
example: admin example: admin
type: string type: string
type: object type: object
api_acladmin.deleteResourceErrorInvalidResourceID:
properties:
details:
example: Resource ID must be positive integer
type: string
error:
example: INVALID_RESOURCE_ID
type: string
type: object
api_acladmin.deleteResourceErrorResourceInUse:
properties:
details:
example: Resource with ID 123 is used and cannot be deleted
type: string
error:
example: FAILED_TO_DELETE_RESOURCE
type: string
type: object
api_acladmin.deleteResourceErrorResourceNotFound:
properties:
details:
example: No resource with ID 123
type: string
error:
example: RESOURCE_NOT_FOUND
type: string
type: object
api_acladmin.deleteRoleErrorInvalidRoleID:
properties:
details:
example: Role ID must be positive integer
type: string
error:
example: INVALID_ROLE_ID
type: string
type: object
api_acladmin.deleteRoleErrorRoleInUse:
properties:
details:
example: Role with ID 123 is assigned to users and cannot be deleted
type: string
error:
example: FAILED_TO_DELETE_ROLE
type: string
type: object
api_acladmin.deleteRoleErrorRoleNotFound:
properties:
details:
example: No role with ID 123
type: string
error:
example: ROLE_NOT_FOUND
type: string
type: object
api_acladmin.errorInternalServerError:
properties:
details:
type: string
error:
type: string
type: object
api_acladmin.errorInvalidRequestBody:
properties:
details:
example: Request body is not valid JSON
type: string
error:
example: INVALID_REQUEST_BODY
type: string
type: object
api_acladmin.getResourceErrorInvalidResourceID:
properties:
details:
example: Resource ID must be positive integer
type: string
error:
example: INVALID_RESOURCE_ID
type: string
type: object
api_acladmin.getResourceErrorResourceNotFound:
properties:
details:
example: No resource with ID 123
type: string
error:
example: RESOURCE_NOT_FOUND
type: string
type: object
api_acladmin.getResourceResponse: api_acladmin.getResourceResponse:
properties: properties:
id: id:
@@ -162,24 +56,6 @@ definitions:
example: html.view example: html.view
type: string type: string
type: object type: object
api_acladmin.getRoleErrorInvalidRoleID:
properties:
details:
example: Role ID must be positive integer
type: string
error:
example: INVALID_ROLE_ID
type: string
type: object
api_acladmin.getRoleErrorRoleNotFound:
properties:
details:
example: No role with ID 123
type: string
error:
example: ROLE_NOT_FOUND
type: string
type: object
api_acladmin.getRoleResponse: api_acladmin.getRoleResponse:
properties: properties:
id: id:
@@ -189,40 +65,16 @@ definitions:
example: admin example: admin
type: string type: string
type: object type: object
api_acladmin.updateResourceErrorInvalidResourceID: api_acladmin.getRoleUser:
properties: properties:
details: userEmail:
example: Resource ID must be positive integer example: admin@triggerssmith.com
type: string type: string
error: userId:
example: INVALID_RESOURCE_ID example: 1
type: string type: integer
type: object userName:
api_acladmin.updateResourceErrorInvalidResourceKey: example: admin
properties:
details:
example: Invalid resource key
type: string
error:
example: FAILED_TO_UPDATE_RESOURCE
type: string
type: object
api_acladmin.updateResourceErrorResourceKeyAlreadyExists:
properties:
details:
example: Resource with key 'html.view' already exists
type: string
error:
example: FAILED_TO_UPDATE_RESOURCE
type: string
type: object
api_acladmin.updateResourceErrorResourceNotFound:
properties:
details:
example: No resource with ID 123
type: string
error:
example: RESOURCE_NOT_FOUND
type: string type: string
type: object type: object
api_acladmin.updateResourceRequest: api_acladmin.updateResourceRequest:
@@ -240,42 +92,6 @@ definitions:
example: html.view example: html.view
type: string type: string
type: object type: object
api_acladmin.updateRoleErrorInvalidRoleID:
properties:
details:
example: Role ID must be positive integer
type: string
error:
example: INVALID_ROLE_ID
type: string
type: object
api_acladmin.updateRoleErrorInvalidRoleName:
properties:
details:
example: Invalid role name
type: string
error:
example: FAILED_TO_UPDATE_ROLE
type: string
type: object
api_acladmin.updateRoleErrorRoleNameAlreadyExists:
properties:
details:
example: Role with name 'admin' already exists
type: string
error:
example: FAILED_TO_UPDATE_ROLE
type: string
type: object
api_acladmin.updateRoleErrorRoleNotFound:
properties:
details:
example: No role with ID 123
type: string
error:
example: ROLE_NOT_FOUND
type: string
type: object
api_acladmin.updateRoleRequest: api_acladmin.updateRoleRequest:
properties: properties:
name: name:
@@ -302,6 +118,7 @@ paths:
"200": "200":
description: OK description: OK
schema: schema:
items:
items: items:
properties: properties:
id: id:
@@ -312,10 +129,11 @@ paths:
type: string type: string
type: object type: object
type: array type: array
type: array
"500": "500":
description: Internal Server Error description: Internal Server Error
schema: schema:
$ref: '#/definitions/api_acladmin.errorInternalServerError' $ref: '#/definitions/api_acladmin.ProblemDetails'
summary: Get all resources summary: Get all resources
tags: tags:
- resources - resources
@@ -339,15 +157,15 @@ paths:
"400": "400":
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/api_acladmin.createResourceErrorInvalidResourceKey' $ref: '#/definitions/api_acladmin.ProblemDetails'
"409": "409":
description: Conflict description: Conflict
schema: schema:
$ref: '#/definitions/api_acladmin.createResourceErrorResourceAlreadyExists' $ref: '#/definitions/api_acladmin.ProblemDetails'
"500": "500":
description: Internal Server Error description: Internal Server Error
schema: schema:
$ref: '#/definitions/api_acladmin.errorInternalServerError' $ref: '#/definitions/api_acladmin.ProblemDetails'
summary: Create resource summary: Create resource
tags: tags:
- resources - resources
@@ -368,19 +186,19 @@ paths:
"400": "400":
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/api_acladmin.deleteResourceErrorInvalidResourceID' $ref: '#/definitions/api_acladmin.ProblemDetails'
"404": "404":
description: Not Found description: Not Found
schema: schema:
$ref: '#/definitions/api_acladmin.deleteResourceErrorResourceNotFound' $ref: '#/definitions/api_acladmin.ProblemDetails'
"409": "409":
description: Conflict description: Conflict
schema: schema:
$ref: '#/definitions/api_acladmin.deleteResourceErrorResourceInUse' $ref: '#/definitions/api_acladmin.ProblemDetails'
"500": "500":
description: Internal Server Error description: Internal Server Error
schema: schema:
$ref: '#/definitions/api_acladmin.errorInternalServerError' $ref: '#/definitions/api_acladmin.ProblemDetails'
summary: Delete resource summary: Delete resource
tags: tags:
- resources - resources
@@ -402,15 +220,15 @@ paths:
"400": "400":
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/api_acladmin.getResourceErrorInvalidResourceID' $ref: '#/definitions/api_acladmin.ProblemDetails'
"404": "404":
description: Not Found description: Not Found
schema: schema:
$ref: '#/definitions/api_acladmin.getResourceErrorResourceNotFound' $ref: '#/definitions/api_acladmin.ProblemDetails'
"500": "500":
description: Internal Server Error description: Internal Server Error
schema: schema:
$ref: '#/definitions/api_acladmin.errorInternalServerError' $ref: '#/definitions/api_acladmin.ProblemDetails'
summary: Get resource by ID summary: Get resource by ID
tags: tags:
- resources - resources
@@ -440,19 +258,19 @@ paths:
"400": "400":
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/api_acladmin.updateResourceErrorInvalidResourceKey' $ref: '#/definitions/api_acladmin.ProblemDetails'
"404": "404":
description: Not Found description: Not Found
schema: schema:
$ref: '#/definitions/api_acladmin.updateResourceErrorResourceNotFound' $ref: '#/definitions/api_acladmin.ProblemDetails'
"409": "409":
description: Conflict description: Conflict
schema: schema:
$ref: '#/definitions/api_acladmin.updateResourceErrorResourceKeyAlreadyExists' $ref: '#/definitions/api_acladmin.ProblemDetails'
"500": "500":
description: Internal Server Error description: Internal Server Error
schema: schema:
$ref: '#/definitions/api_acladmin.errorInternalServerError' $ref: '#/definitions/api_acladmin.ProblemDetails'
summary: Update resource summary: Update resource
tags: tags:
- resources - resources
@@ -464,6 +282,7 @@ paths:
"200": "200":
description: OK description: OK
schema: schema:
items:
items: items:
properties: properties:
id: id:
@@ -474,10 +293,11 @@ paths:
type: string type: string
type: object type: object
type: array type: array
type: array
"500": "500":
description: Internal Server Error description: Internal Server Error
schema: schema:
$ref: '#/definitions/api_acladmin.errorInternalServerError' $ref: '#/definitions/api_acladmin.ProblemDetails'
summary: Get all roles summary: Get all roles
tags: tags:
- roles - roles
@@ -501,19 +321,15 @@ paths:
"400": "400":
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/api_acladmin.errorInvalidRequestBody' $ref: '#/definitions/api_acladmin.ProblemDetails'
"401":
description: Unauthorized
schema:
$ref: '#/definitions/api_acladmin.createRoleErrorInvalidRoleName'
"409": "409":
description: Conflict description: Conflict
schema: schema:
$ref: '#/definitions/api_acladmin.createRoleErrorRoleAlreadyExists' $ref: '#/definitions/api_acladmin.ProblemDetails'
"500": "500":
description: Internal Server Error description: Internal Server Error
schema: schema:
$ref: '#/definitions/api_acladmin.errorInternalServerError' $ref: '#/definitions/api_acladmin.ProblemDetails'
summary: Create role summary: Create role
tags: tags:
- roles - roles
@@ -534,19 +350,19 @@ paths:
"400": "400":
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/api_acladmin.deleteRoleErrorInvalidRoleID' $ref: '#/definitions/api_acladmin.ProblemDetails'
"404": "404":
description: Not Found description: Not Found
schema: schema:
$ref: '#/definitions/api_acladmin.deleteRoleErrorRoleNotFound' $ref: '#/definitions/api_acladmin.ProblemDetails'
"409": "409":
description: Conflict description: Conflict
schema: schema:
$ref: '#/definitions/api_acladmin.deleteRoleErrorRoleInUse' $ref: '#/definitions/api_acladmin.ProblemDetails'
"500": "500":
description: Internal Server Error description: Internal Server Error
schema: schema:
$ref: '#/definitions/api_acladmin.errorInternalServerError' $ref: '#/definitions/api_acladmin.ProblemDetails'
summary: Delete role summary: Delete role
tags: tags:
- roles - roles
@@ -568,15 +384,15 @@ paths:
"400": "400":
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/api_acladmin.getRoleErrorInvalidRoleID' $ref: '#/definitions/api_acladmin.ProblemDetails'
"404": "404":
description: Not Found description: Not Found
schema: schema:
$ref: '#/definitions/api_acladmin.getRoleErrorRoleNotFound' $ref: '#/definitions/api_acladmin.ProblemDetails'
"500": "500":
description: Internal Server Error description: Internal Server Error
schema: schema:
$ref: '#/definitions/api_acladmin.errorInternalServerError' $ref: '#/definitions/api_acladmin.ProblemDetails'
summary: Get role by ID summary: Get role by ID
tags: tags:
- roles - roles
@@ -606,20 +422,55 @@ paths:
"400": "400":
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/api_acladmin.updateRoleErrorInvalidRoleName' $ref: '#/definitions/api_acladmin.ProblemDetails'
"404": "404":
description: Not Found description: Not Found
schema: schema:
$ref: '#/definitions/api_acladmin.updateRoleErrorRoleNotFound' $ref: '#/definitions/api_acladmin.ProblemDetails'
"409": "409":
description: Conflict description: Conflict
schema: schema:
$ref: '#/definitions/api_acladmin.updateRoleErrorRoleNameAlreadyExists' $ref: '#/definitions/api_acladmin.ProblemDetails'
"500": "500":
description: Internal Server Error description: Internal Server Error
schema: schema:
$ref: '#/definitions/api_acladmin.errorInternalServerError' $ref: '#/definitions/api_acladmin.ProblemDetails'
summary: Update role summary: Update role
tags: tags:
- roles - roles
/api/acl/roles/{roleId}/users:
get:
parameters:
- description: Role ID
example: 1
in: path
name: roleId
required: true
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
items:
$ref: '#/definitions/api_acladmin.getRoleUser'
type: array
type: array
"400":
description: Bad Request
schema:
$ref: '#/definitions/api_acladmin.ProblemDetails'
"404":
description: Not Found
schema:
$ref: '#/definitions/api_acladmin.ProblemDetails'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/api_acladmin.ProblemDetails'
summary: Get role users
tags:
- roles
swagger: "2.0" swagger: "2.0"

View File

@@ -1,11 +1,13 @@
package acl package acl
import "git.oblat.lv/alex/triggerssmith/internal/user"
type UserRole struct { type UserRole struct {
UserID uint `gorm:"index;not null;uniqueIndex:ux_user_role"` UserID uint `gorm:"index;not null;uniqueIndex:ux_user_role"`
RoleID uint `gorm:"index;not null;uniqueIndex:ux_user_role"` RoleID uint `gorm:"index;not null;uniqueIndex:ux_user_role"`
Role Role `gorm:"constraint:OnDelete:CASCADE;foreignKey:RoleID;references:ID" json:"role"` Role Role `gorm:"constraint:OnDelete:CASCADE;foreignKey:RoleID;references:ID" json:"role"`
//User user.User `gorm:"constraint:OnDelete:CASCADE;foreignKey:UserID;references:ID"` User user.User `gorm:"constraint:OnDelete:CASCADE;foreignKey:UserID;references:ID"`
} }
type Resource struct { type Resource struct {
@@ -18,7 +20,7 @@ type Role struct {
Name string `gorm:"unique;not null" json:"name"` Name string `gorm:"unique;not null" json:"name"`
Resources []Resource `gorm:"many2many:role_resources" json:"resources"` Resources []Resource `gorm:"many2many:role_resources" json:"resources"`
//Users []user.User `gorm:"many2many:user_roles"` Users []user.User `gorm:"many2many:user_roles"`
} }
type RoleResource struct { type RoleResource struct {

View File

@@ -16,7 +16,7 @@ func (s *Service) GetRoles() ([]Role, error) {
} }
var roles []Role var roles []Role
if err := s.db.Preload("Resources").Order("id").Find(&roles).Error; err != nil { if err := s.db.Preload("Resources").Preload("Users").Order("id").Find(&roles).Error; err != nil {
return nil, fmt.Errorf("db error: %w", err) return nil, fmt.Errorf("db error: %w", err)
} }
@@ -60,7 +60,7 @@ func (s *Service) GetRoleByID(roleID uint) (*Role, error) {
return nil, ErrNotInitialized return nil, ErrNotInitialized
} }
var role Role var role Role
err := s.db.Preload("Resources").First(&role, roleID).Error err := s.db.Preload("Resources").Preload("Users").First(&role, roleID).Error
if err != nil { if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrRoleNotFound return nil, ErrRoleNotFound

View File

@@ -1,7 +1,6 @@
package user package user
import ( import (
"git.oblat.lv/alex/triggerssmith/internal/acl"
"gorm.io/gorm" "gorm.io/gorm"
) )
@@ -10,6 +9,6 @@ type User struct {
Username string `gorm:"uniqueIndex;not null"` Username string `gorm:"uniqueIndex;not null"`
Email string `gorm:"uniqueIndex;not null"` Email string `gorm:"uniqueIndex;not null"`
Password string `gorm:"not null"` Password string `gorm:"not null"`
Roles []acl.Role `gorm:"many2many:user_roles"` //Roles []acl.Role `gorm:"many2many:user_roles"`
DeletedAt gorm.DeletedAt `gorm:"index"` DeletedAt gorm.DeletedAt `gorm:"index"`
} }