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
103 lines
2.4 KiB
Go
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
|
|
}
|