Files
ITWorx Pulse release export bd774932d5
Public source validation / validate (push) Failing after 3m8s
Publish ITWorx Pulse source
2026-09-03 02:09:19 +02:00

88 lines
2.9 KiB
Go

package onboardingapi
import (
"context"
"encoding/json"
"io"
"net/http"
"strings"
"github.com/itworx/pulse/internal/audit"
"github.com/itworx/pulse/internal/auth"
"github.com/itworx/pulse/internal/correlation"
"github.com/itworx/pulse/internal/onboarding"
"github.com/itworx/pulse/internal/problem"
)
type Service interface {
Status(context.Context) (onboarding.Status, error)
Complete(context.Context, onboarding.Choice) (onboarding.Status, error)
}
type Handler struct {
Service Service
Audit audit.Store
}
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
principal, ok := auth.PrincipalFromContext(r.Context())
if !ok {
fail(w, r, http.StatusUnauthorized, "UNAUTHORIZED", "Authentication required.")
return
}
if strings.TrimSuffix(r.URL.Path, "/") != "/api/v1/onboarding" {
fail(w, r, http.StatusNotFound, "NOT_FOUND", "Onboarding not found.")
return
}
switch r.Method {
case http.MethodGet:
status, err := h.Service.Status(r.Context())
if err != nil {
fail(w, r, http.StatusServiceUnavailable, "ONBOARDING_UNAVAILABLE", "Onboarding status is unavailable.")
return
}
write(w, http.StatusOK, status)
case http.MethodPost:
if !auth.Allows(principal.Role, auth.PermissionAdmin) {
fail(w, r, http.StatusForbidden, "FORBIDDEN", "Only administrators can complete onboarding.")
return
}
var choice onboarding.Choice
decoder := json.NewDecoder(http.MaxBytesReader(w, r.Body, 16<<10))
decoder.DisallowUnknownFields()
if err := decoder.Decode(&choice); err != nil {
fail(w, r, http.StatusBadRequest, "INVALID_CHOICES", "Onboarding choices are invalid.")
return
}
var extra any
if err := decoder.Decode(&extra); err != io.EOF {
fail(w, r, http.StatusBadRequest, "INVALID_CHOICES", "Onboarding choices are invalid.")
return
}
status, err := h.Service.Complete(r.Context(), choice)
if err != nil {
fail(w, r, http.StatusBadRequest, "ONBOARDING_FAILED", "Onboarding kon niet veilig worden afgerond.")
return
}
if h.Audit != nil {
if err := h.Audit.Append(r.Context(), audit.Event{Actor: principal.Subject, Action: "onboarding.complete", ResourceType: "system", ResourceID: "00000000-0000-0000-0000-000000000000", Result: "success", CorrelationID: correlation.FromContext(r.Context())}); err != nil {
fail(w, r, http.StatusServiceUnavailable, "AUDIT_UNAVAILABLE", "The onboarding audit event could not be recorded.")
return
}
}
write(w, http.StatusOK, status)
default:
fail(w, r, http.StatusMethodNotAllowed, "METHOD_NOT_ALLOWED", "This method is not supported.")
}
}
func fail(w http.ResponseWriter, r *http.Request, status int, code, detail string) {
problem.Write(w, r, status, code, http.StatusText(status), detail, nil)
}
func write(w http.ResponseWriter, status int, value any) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
_ = json.NewEncoder(w).Encode(value)
}