mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-07-17 08:19:53 +00:00
236 lines
6.3 KiB
Go
236 lines
6.3 KiB
Go
package api
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/pocket-id/pocket-id/backend/internal/dto"
|
|
"github.com/pocket-id/pocket-id/backend/internal/utils"
|
|
)
|
|
|
|
type handler struct {
|
|
service *Service
|
|
}
|
|
|
|
func newHandler(service *Service) *handler {
|
|
return &handler{service: service}
|
|
}
|
|
|
|
// list godoc
|
|
// @Summary List APIs
|
|
// @Description Get a paginated list of APIs with optional search and sorting
|
|
// @Tags APIs
|
|
// @Produce json
|
|
// @Param search query string false "Search term to filter APIs by name or resource"
|
|
// @Param pagination[page] query int false "Page number for pagination" default(1)
|
|
// @Param pagination[limit] query int false "Number of items per page" default(20)
|
|
// @Param sort[column] query string false "Column to sort by"
|
|
// @Param sort[direction] query string false "Sort direction (asc or desc)" default("asc")
|
|
// @Success 200 {object} dto.Paginated[apiListItemDto]
|
|
// @Router /api/apis [get]
|
|
func (h *handler) list(c *gin.Context) {
|
|
search := c.Query("search")
|
|
listRequestOptions := utils.ParseListRequestOptions(c)
|
|
|
|
apis, pagination, err := h.service.List(c.Request.Context(), search, listRequestOptions)
|
|
if err != nil {
|
|
_ = c.Error(err)
|
|
return
|
|
}
|
|
|
|
items := make([]apiResponseDto, len(apis))
|
|
for i, api := range apis {
|
|
var item apiResponseDto
|
|
if err := dto.MapStruct(api, &item); err != nil {
|
|
_ = c.Error(err)
|
|
return
|
|
}
|
|
item.Resource = api.Audience
|
|
items[i] = item
|
|
}
|
|
|
|
c.JSON(http.StatusOK, dto.Paginated[apiResponseDto]{
|
|
Data: items,
|
|
Pagination: pagination,
|
|
})
|
|
}
|
|
|
|
// get godoc
|
|
// @Summary Get API by ID
|
|
// @Description Retrieve a single API including its permissions
|
|
// @Tags APIs
|
|
// @Produce json
|
|
// @Param id path string true "API ID"
|
|
// @Success 200 {object} apiResponseDto
|
|
// @Router /api/apis/{id} [get]
|
|
func (h *handler) get(c *gin.Context) {
|
|
api, err := h.service.Get(c.Request.Context(), nil, c.Param("id"))
|
|
if err != nil {
|
|
_ = c.Error(err)
|
|
return
|
|
}
|
|
|
|
h.respond(c, http.StatusOK, api)
|
|
}
|
|
|
|
// create godoc
|
|
// @Summary Create API
|
|
// @Description Create a new API resource server
|
|
// @Tags APIs
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param api body apiCreateDto true "API information"
|
|
// @Success 201 {object} apiResponseDto "Created API"
|
|
// @Router /api/apis [post]
|
|
func (h *handler) create(c *gin.Context) {
|
|
var input apiCreateDto
|
|
if err := dto.ShouldBindWithNormalizedJSON(c, &input); err != nil {
|
|
_ = c.Error(err)
|
|
return
|
|
}
|
|
|
|
api, err := h.service.Create(c.Request.Context(), input)
|
|
if err != nil {
|
|
_ = c.Error(err)
|
|
return
|
|
}
|
|
|
|
h.respond(c, http.StatusCreated, api)
|
|
}
|
|
|
|
// update godoc
|
|
// @Summary Update API
|
|
// @Description Update an existing API by ID
|
|
// @Tags APIs
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param id path string true "API ID"
|
|
// @Param api body apiUpdateDto true "API information"
|
|
// @Success 200 {object} apiResponseDto "Updated API"
|
|
// @Router /api/apis/{id} [put]
|
|
func (h *handler) update(c *gin.Context) {
|
|
var input apiUpdateDto
|
|
if err := dto.ShouldBindWithNormalizedJSON(c, &input); err != nil {
|
|
_ = c.Error(err)
|
|
return
|
|
}
|
|
|
|
api, err := h.service.Update(c.Request.Context(), c.Param("id"), input)
|
|
if err != nil {
|
|
_ = c.Error(err)
|
|
return
|
|
}
|
|
|
|
h.respond(c, http.StatusOK, api)
|
|
}
|
|
|
|
// delete godoc
|
|
// @Summary Delete API
|
|
// @Description Delete an API by ID
|
|
// @Tags APIs
|
|
// @Param id path string true "API ID"
|
|
// @Success 204 "No Content"
|
|
// @Router /api/apis/{id} [delete]
|
|
func (h *handler) delete(c *gin.Context) {
|
|
if err := h.service.Delete(c.Request.Context(), c.Param("id")); err != nil {
|
|
_ = c.Error(err)
|
|
return
|
|
}
|
|
|
|
c.Status(http.StatusNoContent)
|
|
}
|
|
|
|
// updatePermissions godoc
|
|
// @Summary Update API permissions
|
|
// @Description Replace the full set of permissions for an API
|
|
// @Tags APIs
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param id path string true "API ID"
|
|
// @Param permissions body apiPermissionsUpdateDto true "Permissions to set"
|
|
// @Success 200 {object} apiResponseDto "Updated API"
|
|
// @Router /api/apis/{id}/permissions [put]
|
|
func (h *handler) updatePermissions(c *gin.Context) {
|
|
var input apiPermissionsUpdateDto
|
|
if err := dto.ShouldBindWithNormalizedJSON(c, &input); err != nil {
|
|
_ = c.Error(err)
|
|
return
|
|
}
|
|
|
|
api, err := h.service.UpdatePermissions(c.Request.Context(), c.Param("id"), input)
|
|
if err != nil {
|
|
_ = c.Error(err)
|
|
return
|
|
}
|
|
|
|
h.respond(c, http.StatusOK, api)
|
|
}
|
|
|
|
// getClientAccess godoc
|
|
// @Summary Get client API access
|
|
// @Description Get the API permissions an OIDC client is allowed to request, split into user-delegated and client (machine-to-machine) access
|
|
// @Tags APIs
|
|
// @Produce json
|
|
// @Param clientId path string true "OIDC Client ID"
|
|
// @Success 200 {object} clientApiAccessDto
|
|
// @Router /api/api-access/{clientId} [get]
|
|
func (h *handler) getClientAccess(c *gin.Context) {
|
|
access, err := h.service.GetClientAPIAccess(c.Request.Context(), c.Param("clientId"))
|
|
if err != nil {
|
|
_ = c.Error(err)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, newClientApiAccessDto(access))
|
|
}
|
|
|
|
// updateClientAccess godoc
|
|
// @Summary Update client API access
|
|
// @Description Replace the API permissions an OIDC client is allowed to request, split into user-delegated and client (machine-to-machine) access
|
|
// @Tags APIs
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param clientId path string true "OIDC Client ID"
|
|
// @Param access body clientApiAccessUpdateDto true "Allowed permission IDs per subject type"
|
|
// @Success 200 {object} clientApiAccessDto
|
|
// @Router /api/api-access/{clientId} [put]
|
|
func (h *handler) updateClientAccess(c *gin.Context) {
|
|
var input clientApiAccessUpdateDto
|
|
err := c.ShouldBindJSON(&input)
|
|
if err != nil {
|
|
_ = c.Error(err)
|
|
return
|
|
}
|
|
|
|
applied, err := h.service.SetClientAPIAccess(c.Request.Context(), c.Param("clientId"), ClientAPIAccess(input))
|
|
if err != nil {
|
|
_ = c.Error(err)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, newClientApiAccessDto(applied))
|
|
}
|
|
|
|
// newClientApiAccessDto always serializes both permission lists as arrays rather than null
|
|
func newClientApiAccessDto(access ClientAPIAccess) clientApiAccessDto {
|
|
dto := clientApiAccessDto(access)
|
|
if dto.UserDelegatedPermissionIDs == nil {
|
|
dto.UserDelegatedPermissionIDs = []string{}
|
|
}
|
|
if dto.ClientPermissionIDs == nil {
|
|
dto.ClientPermissionIDs = []string{}
|
|
}
|
|
return dto
|
|
}
|
|
|
|
func (h *handler) respond(c *gin.Context, status int, api API) {
|
|
var responseDto apiResponseDto
|
|
if err := dto.MapStruct(api, &responseDto); err != nil {
|
|
_ = c.Error(err)
|
|
return
|
|
}
|
|
responseDto.Resource = api.Audience
|
|
c.JSON(status, responseDto)
|
|
}
|