Files
nexarch/dms/internal/openapi/openapi.go
patrick 5274226d37
CI / Core-Modul (Build/Vet/Test) (push) Canceled after 0s
CI / DMS Security-Test-Gate (AC5, Merge-Gate) (push) Canceled after 0s
CI / DMS-Modul (Lint/Build/Test/Coverage/Artefakt) (push) Canceled after 0s
CI / DMS-Modul Supply-Chain-Scan (govulncheck) (push) Canceled after 0s
CI / Web (dms-browser) (push) Canceled after 0s
CI / Web (pdf-viewer) (push) Canceled after 0s
CI / Web (shl) (push) Canceled after 0s
INT-01: gofmt
2026-09-20 23:50:51 +02:00

103 lines
2.4 KiB
Go

// Package openapi haelt die Schnittstellenbeschreibung der DMS-API und leitet
// daraus die Rechte-Zuordnung je Route ab (eine Quelle fuer Doku und Policy).
package openapi
import (
_ "embed"
"encoding/json"
"net/http"
"sort"
"strings"
)
//go:embed openapi.json
var document []byte
type Operation struct {
Method string
Path string
Permission string
Public bool
segments []string
}
var operations = mustParse()
func mustParse() []Operation {
var doc struct {
Paths map[string]map[string]struct {
Permission string `json:"x-permission"`
Security *[]any `json:"security"`
} `json:"paths"`
}
if err := json.Unmarshal(document, &doc); err != nil {
panic("openapi.json ungueltig: " + err.Error())
}
var out []Operation
for path, methods := range doc.Paths {
for m, o := range methods {
out = append(out, Operation{
Method: strings.ToUpper(m), Path: path, Permission: o.Permission,
Public: o.Security != nil && len(*o.Security) == 0,
segments: strings.Split(strings.Trim(path, "/"), "/"),
})
}
}
sort.Slice(out, func(i, j int) bool {
if out[i].Path != out[j].Path {
return out[i].Path < out[j].Path
}
return out[i].Method < out[j].Method
})
return out
}
func Operations() []Operation { return append([]Operation(nil), operations...) }
func Document() []byte { return document }
func Handler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("X-Content-Type-Options", "nosniff")
_, _ = w.Write(document)
})
}
// Match findet die Operation zu Methode und Pfad (relativ zum Praefix);
// feste Segmente gehen vor Platzhaltern wie im http.ServeMux.
func Match(method, path string) (Operation, bool) {
seg := strings.Split(strings.Trim(path, "/"), "/")
best, bestScore := Operation{}, -1
for _, op := range operations {
if op.Method != method || len(op.segments) != len(seg) {
continue
}
score, ok := 0, true
for i, s := range op.segments {
switch {
case strings.HasPrefix(s, "{"):
case s == seg[i]:
score++
default:
ok = false
}
}
if ok && score > bestScore {
best, bestScore = op, score
}
}
return best, bestScore >= 0
}
// PublicPaths sind Pfade ohne Nutzeridentitaet (Signatur bzw. Dienst-zu-Dienst).
func PublicPaths() map[string]bool {
out := map[string]bool{}
for _, op := range operations {
if op.Public {
out[op.Path] = true
}
}
return out
}