kratos/courier/handler.go

213 lines
5.0 KiB
Go

// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package courier
import (
"net/http"
"github.com/pkg/errors"
"github.com/ory/kratos/x/nosurfx"
"github.com/ory/kratos/x/redir"
"github.com/gofrs/uuid"
"github.com/ory/herodot"
keysetpagination "github.com/ory/x/pagination/keysetpagination_v2"
"github.com/ory/kratos/driver/config"
"github.com/ory/kratos/x"
)
const (
AdminRouteCourier = "/courier"
AdminRouteListMessages = AdminRouteCourier + "/messages"
AdminRouteGetMessage = AdminRouteCourier + "/messages/{msgID}"
)
type (
handlerDependencies interface {
x.WriterProvider
x.LoggingProvider
nosurfx.CSRFProvider
PersistenceProvider
config.Provider
}
Handler struct {
r handlerDependencies
}
HandlerProvider interface {
CourierHandler() *Handler
}
)
func NewHandler(r handlerDependencies) *Handler {
return &Handler{r: r}
}
func (h *Handler) RegisterPublicRoutes(public *x.RouterPublic) {
h.r.CSRFHandler().IgnoreGlobs(x.AdminPrefix+AdminRouteListMessages, AdminRouteListMessages)
public.GET(x.AdminPrefix+AdminRouteListMessages, redir.RedirectToAdminRoute(h.r))
public.GET(x.AdminPrefix+AdminRouteGetMessage, redir.RedirectToAdminRoute(h.r))
}
func (h *Handler) RegisterAdminRoutes(admin *x.RouterAdmin) {
admin.GET(AdminRouteListMessages, h.listCourierMessages)
admin.GET(AdminRouteGetMessage, h.getCourierMessage)
}
// Paginated Courier Message List Response
//
// swagger:response listCourierMessages
//
//nolint:deadcode,unused
//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions
type listCourierMessagesResponse struct {
keysetpagination.ResponseHeaders
// List of identities
//
// in:body
Body []Message
}
// Paginated List Courier Message Parameters
//
// swagger:parameters listCourierMessages
type ListCourierMessagesParameters struct {
keysetpagination.RequestParameters
// Status filters out messages based on status.
// If no value is provided, it doesn't take effect on filter.
//
// required: false
// in: query
Status *MessageStatus `json:"status"`
// Recipient filters out messages based on recipient.
// If no value is provided, it doesn't take effect on filter.
//
// required: false
// in: query
Recipient string `json:"recipient"`
}
// swagger:route GET /admin/courier/messages courier listCourierMessages
//
// # List Messages
//
// Lists all messages by given status and recipient.
//
// Produces:
// - application/json
//
// Security:
// oryAccessToken:
//
// Schemes: http, https
//
// Responses:
// 200: listCourierMessages
// 400: errorGeneric
// default: errorGeneric
func (h *Handler) listCourierMessages(w http.ResponseWriter, r *http.Request) {
keys := h.r.Config().SecretsPagination(r.Context())
filter, paginator, err := parseMessagesFilter(r, keys)
if err != nil {
h.r.Writer().WriteErrorCode(w, r, http.StatusBadRequest, err)
return
}
l, nextPage, err := h.r.CourierPersister().ListMessages(r.Context(), filter, paginator)
if err != nil {
h.r.Writer().WriteError(w, r, err)
return
}
if !h.r.Config().IsInsecureDevMode(r.Context()) {
for i := range l {
l[i].Body = "<redacted-unless-dev-mode>"
}
}
u := *r.URL
keysetpagination.SetLinkHeader(w, keys, &u, nextPage)
h.r.Writer().Write(w, r, l)
}
func parseMessagesFilter(r *http.Request, keys [][32]byte) (ListCourierMessagesParameters, []keysetpagination.Option, error) {
var status *MessageStatus
if r.URL.Query().Has("status") {
ms, err := ToMessageStatus(r.URL.Query().Get("status"))
if err != nil {
return ListCourierMessagesParameters{}, nil, errors.WithStack(err)
}
status = &ms
}
opts, err := keysetpagination.ParseQueryParams(keys, r.URL.Query())
if err != nil {
return ListCourierMessagesParameters{}, nil, errors.WithStack(err)
}
return ListCourierMessagesParameters{
Status: status,
Recipient: r.URL.Query().Get("recipient"),
}, opts, nil
}
// Get Courier Message Parameters
//
// swagger:parameters getCourierMessage
//
//nolint:deadcode,unused
//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions
type getCourierMessage struct {
// MessageID is the ID of the message.
//
// required: true
// in: path
MessageID string `json:"id"`
}
// swagger:route GET /admin/courier/messages/{id} courier getCourierMessage
//
// # Get a Message
//
// Gets a specific messages by the given ID.
//
// Produces:
// - application/json
//
// Security:
// oryAccessToken:
//
// Schemes: http, https
//
// Responses:
// 200: message
// 400: errorGeneric
// default: errorGeneric
func (h *Handler) getCourierMessage(w http.ResponseWriter, r *http.Request) {
msgID, err := uuid.FromString(r.PathValue("msgID"))
if err != nil {
h.r.Writer().WriteError(w, r, herodot.ErrBadRequest.WithError(err.Error()).WithDebugf("could not parse parameter {id} as UUID, got %s", r.PathValue("id")))
return
}
message, err := h.r.CourierPersister().FetchMessage(r.Context(), msgID)
if err != nil {
h.r.Writer().WriteError(w, r, err)
return
}
if !h.r.Config().IsInsecureDevMode(r.Context()) {
message.Body = "<redacted-unless-dev-mode>"
}
h.r.Writer().Write(w, r, message)
}