Files
upstream-ory-kratos/courier/handler.go
upstream-mirror c60cc7ca75
Some checks failed
CI / Run tests and lints (push) Failing after 29s
CI / Build CLI docs (push) Has been skipped
CI / Run Playwright end-to-end tests (mysql) (push) Failing after 3s
CI / Run Playwright end-to-end tests (postgres) (push) Failing after 2s
CI / Run Playwright end-to-end tests (sqlite) (push) Failing after 3s
CodeQL / Analyze (go) (push) Failing after 4s
CodeQL / Analyze (javascript) (push) Failing after 3s
Docker Image Scanners / scanners (push) Failing after 4s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Run end-to-end tests (postgres) (push) Failing after 19s
CI / Run end-to-end tests (sqlite) (push) Failing after 2s
CI / Run Playwright end-to-end tests (cockroach) (push) Failing after 2s
CI / Draft newsletter (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
snapshot of https://github.com/ory/kratos.git (master, 2026-09-15; blobs > 131072B omitted)
2026-09-15 21:19:12 +05:00

220 lines
5.4 KiB
Go

// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package courier
import (
"net/http"
"github.com/gofrs/uuid"
"github.com/pkg/errors"
"github.com/ory/herodot"
"github.com/ory/kratos/driver/config"
"github.com/ory/kratos/x/nosurfx"
"github.com/ory/kratos/x/redir"
"github.com/ory/x/httprouterx"
"github.com/ory/x/httpx"
"github.com/ory/x/logrusx"
keysetpagination "github.com/ory/x/pagination/keysetpagination_v2"
)
const (
AdminRouteCourier = "/courier"
AdminRouteListMessages = AdminRouteCourier + "/messages"
AdminRouteGetMessage = AdminRouteCourier + "/messages/{msgID}"
)
type (
handlerDependencies interface {
httpx.WriterProvider
logrusx.Provider
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 *httprouterx.RouterPublic) {
h.r.CSRFHandler().IgnoreGlobs(httprouterx.AdminPrefix+AdminRouteListMessages, AdminRouteListMessages)
public.GET(httprouterx.AdminPrefix+AdminRouteListMessages, redir.RedirectToAdminRoute(h.r))
public.GET(httprouterx.AdminPrefix+AdminRouteGetMessage, redir.RedirectToAdminRoute(h.r))
}
func (h *Handler) RegisterAdminRoutes(admin *httprouterx.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
//
// Extensions:
// x-ory-ratelimit-bucket: kratos-admin-low
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
}
messages, 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 messages {
messages[i].Body = "<redacted-unless-dev-mode>"
messages[i].Subject = "<redacted-unless-dev-mode>"
}
}
u := *r.URL
keysetpagination.SetLinkHeader(w, keys, &u, nextPage)
h.r.Writer().Write(w, r, messages)
}
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
//
// Extensions:
// x-ory-ratelimit-bucket: kratos-admin-medium
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>"
message.Subject = "<redacted-unless-dev-mode>"
}
h.r.Writer().Write(w, r, message)
}