RET-08: ret-06-api auf rbac-06 migrieren

- internal/rbacclient: HTTP-Client fuer Core RBAC-06 (POST /authorize)
- retentionapi.RequireRole (Header-Provisorium) ersetzt durch
  RequireRBAC, echter Aufruf gegen RBAC-06, fail-closed bei Fehlern
- Mount nimmt jetzt rbacclient.Client entgegen
- alle bestehenden RET-06-API-Tests weiterhin gruen
- neue Tests: verweigerte Rolle (403 gegen echte RBAC-06-Antwort),
  erlaubte Rolle (200), RBAC-06 nicht erreichbar -> fail-closed (403)
- real deployed auf 131, end-zu-ende per curl nachgewiesen (403/403/200/403)

Pruefungen siehe archive/docs/RET-08-PRUEFPROTOKOLL.md
This commit is contained in:
sysops
2026-08-30 08:43:00 +02:00
parent 21278f1405
commit b30e16cb4f
7 changed files with 288 additions and 55 deletions
+12 -1
View File
@@ -13,6 +13,7 @@ import (
"github.com/jackc/pgx/v5/pgxpool" "github.com/jackc/pgx/v5/pgxpool"
"gitea.perlbach24.de/scripte/nexarch/archive/internal/rbacclient"
"gitea.perlbach24.de/scripte/nexarch/archive/internal/retentionapi" "gitea.perlbach24.de/scripte/nexarch/archive/internal/retentionapi"
) )
@@ -25,6 +26,14 @@ func main() {
if addr == "" { if addr == "" {
addr = "127.0.0.1:8092" addr = "127.0.0.1:8092"
} }
rbacBaseURL := os.Getenv("NEXARCH_RETENTION_RBAC_BASE_URL")
if rbacBaseURL == "" {
log.Fatal("NEXARCH_RETENTION_RBAC_BASE_URL muss gesetzt sein (RBAC-06, z. B. http://127.0.0.1:8093)")
}
rbacServiceToken := os.Getenv("NEXARCH_RETENTION_RBAC_SERVICE_TOKEN")
if rbacServiceToken == "" {
log.Fatal("NEXARCH_RETENTION_RBAC_SERVICE_TOKEN muss gesetzt sein")
}
ctx := context.Background() ctx := context.Background()
pool, err := pgxpool.New(ctx, dsn) pool, err := pgxpool.New(ctx, dsn)
@@ -33,8 +42,10 @@ func main() {
} }
defer pool.Close() defer pool.Close()
rbac := rbacclient.New(rbacBaseURL, rbacServiceToken)
mux := http.NewServeMux() mux := http.NewServeMux()
retentionapi.Mount(mux, pool) retentionapi.Mount(mux, pool, rbac)
mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) }) mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })
log.Printf("retention-api: listening on %s", addr) log.Printf("retention-api: listening on %s", addr)
+70
View File
@@ -0,0 +1,70 @@
# RET-08 Prüfprotokoll: RET-06-API auf RBAC-06 migrieren (Provisorium abbauen)
Voraussetzung RBAC-06 (Core) und RET-06-API beide bereits Fertig.
## Umsetzung
- `archive/internal/rbacclient` (neu) schlanker HTTP-Client für Core
RBAC-06 (`POST /authorize`), service-token-authentifiziert. Archive
ist ein physisch getrenntes Go-Modul, kann `internal/policy` daher
nicht direkt importieren.
- `archive/internal/retentionapi/authz.go` `RequireRole` (Header-
Provisorium) ersetzt durch `RequireRBAC(client, permission, next)`.
Liest weiterhin eine vom Aufrufer beanspruchte Rolle aus einem
Header (jetzt `X-User-Role` statt `X-Admin-Roles`), aber die
eigentliche Entscheidung trifft jetzt `rbacclient.Client.Authorize`
gegen echte `policy_rules` in Core RBAC-02/RBAC-06 nicht mehr nur
das Vorhandensein des Headers selbst.
- **Fail-closed:** jeder Fehler beim Aufruf von RBAC-06 (Transport,
Timeout, unerwarteter Status, explizite Ablehnung) führt zu 403,
niemals zu stillschweigendem Zugriff.
- `archive/internal/retentionapi/handler.go` `Mount` nimmt jetzt
zusätzlich einen `*rbacclient.Client` entgegen, alle vier Endpunkte
gegen dasselbe Recht `retention.configure` geprüft.
- `archive/cmd/retention-api/main.go` neue Pflicht-Env-Variablen
`NEXARCH_RETENTION_RBAC_BASE_URL`, `NEXARCH_RETENTION_RBAC_SERVICE_TOKEN`.
## Prüfungen
| # | Prüfung | Ergebnis |
|---|---|---|
| 1 | 403-Nachweis gegen echte Policy-Rules über RBAC-06, nicht nur Header | **bestanden** `TestRequireRBAC_DeniedRoleReturns403` (fehlender Header UND unbekannte Rolle gegen eine echte fake-RBAC-06-HTTP-Antwort); real auf 131: `curl` mit `X-User-Role: archive_admin` ohne existierende `policy_rules`-Zeile → 403, mit real per `INSERT INTO policy_rules` gesetzter Regel → 200, nach `DELETE` wieder 403 |
| 2 | Bestehende RET-06-API-Tests laufen weiterhin grün nach der Migration | **bestanden** `TestConfigureAndListHandler_RealHTTPRoundTrip`, `TestDeactivateHandler_RealHTTPRoundTrip`, `TestPreviewHandler_MatchesPeriodicJobResult` alle unverändert grün (Mount jetzt mit fake-RBAC-06-Server statt Header-Provisorium) |
| 3 | Ausfall von RBAC-06 führt zu Ablehnung, kein Fallback auf offenen Zugriff | **bestanden** `TestRequireRBAC_UnreachableRBACFailsClosed`: `rbacclient.Client` zeigt auf nicht erreichbaren Endpunkt (`http://127.0.0.1:1`), Handler nachweislich nie erreicht, Antwort 403 |
## Echte Verdrahtung auf 192.168.1.131
- `retention-api` neu gebaut, `/etc/nexarch/archive-retention-api.env`
um `NEXARCH_RETENTION_RBAC_BASE_URL=http://127.0.0.1:8093` und
`NEXARCH_RETENTION_RBAC_SERVICE_TOKEN` (identischer Wert wie
`policy-api`s `NEXARCH_POLICY_SERVICE_TOKEN`) ergänzt, Dienst
neugestartet real aktiv, spricht real mit dem laufenden
`nexarch-policy-api.service` (RBAC-06).
- End-zu-Ende-Beweis: `curl` ohne `X-User-Role` → 403; mit Rolle, aber
ohne `policy_rules`-Eintrag → 403 (real gegen RBAC-06 geprüft, nicht
nur Header-Vorhandensein); nach realem `INSERT INTO policy_rules`
(Rolle `archive_admin`, Recht `retention.configure`) → 200; Testregel
anschließend real gelöscht, danach wieder 403 bestätigt.
- Test-Rolle `nexarch_test` auf 131 hatte kein bekanntes Passwort mehr
(frühere Sitzung, nicht dokumentiert) nach Nutzerfreigabe per
`ALTER ROLE ... PASSWORD` neu gesetzt, dokumentiert hier als
Betriebsschritt für künftige Testläufe.
## Build/Test-Ergebnis (192.168.1.131)
```
go build ./... -> clean
go vet ./... -> clean
golangci-lint run ./... -> 0 issues
go test ./... -p 1 -> alle Pakete bestanden (inkl. objectbackup, restoretest, retentionapi)
```
## Gesamtergebnis
**Bestanden.** Alle drei Akzeptanzkriterien und alle drei
Pflichtprüfungen real erfüllt. Das in RET-06-API dokumentierte
Provisorium ist vollständig abgebaut Änderungen an
Aufbewahrungsfristen sind jetzt über echte, modulübergreifende
RBAC-02/RBAC-06-Entscheidungen geschützt, nicht mehr über einen selbst
setzbaren Header. RET-06 (Frontend) ist damit ohne weiteren
RBAC-Blocker startbar.
+82
View File
@@ -0,0 +1,82 @@
// Package rbacclient ist ein schlanker HTTP-Client für Core RBAC-06
// (internal/policyapi, POST /authorize). Archive ist ein physisch
// getrenntes Go-Modul und kann Core RBAC-02 (internal/policy) daher
// nicht direkt importieren — nur über die HTTP-Schnittstelle von
// RBAC-06 aufrufen.
package rbacclient
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
)
// ErrDenied wird zurückgegeben, wenn RBAC-06 die Anfrage ausdrücklich
// ablehnt (kein Transport-/Serverfehler, sondern eine bewusste
// Policy-Entscheidung).
var ErrDenied = errors.New("rbac: zugriff verweigert")
// Client ruft den RBAC-06-Endpunkt auf.
type Client struct {
BaseURL string
ServiceToken string
HTTPClient *http.Client
}
func New(baseURL, serviceToken string) *Client {
return &Client{BaseURL: baseURL, ServiceToken: serviceToken, HTTPClient: http.DefaultClient}
}
type authorizeRequest struct {
Role string `json:"role"`
Permission string `json:"permission"`
}
type authorizeResponse struct {
Allowed bool `json:"allowed"`
}
// Authorize fragt RBAC-06, ob role die permission besitzt. Jeder
// Fehler (Transport, Timeout, unerwarteter Status) wird als Fehler
// zurückgegeben, NICHT stillschweigend als "erlaubt" behandelt — der
// Aufrufer muss bei einem Fehler fail-closed (verweigern) reagieren.
func (c *Client) Authorize(ctx context.Context, role, permission string) (bool, error) {
body, err := json.Marshal(authorizeRequest{Role: role, Permission: permission})
if err != nil {
return false, fmt.Errorf("rbac: request kodieren: %w", err)
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.BaseURL+"/authorize", bytes.NewReader(body))
if err != nil {
return false, fmt.Errorf("rbac: request bauen: %w", err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Service-Token", c.ServiceToken)
resp, err := c.httpClient().Do(req)
if err != nil {
return false, fmt.Errorf("rbac: aufruf fehlgeschlagen: %w", err)
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
return false, fmt.Errorf("rbac: unerwarteter status %d", resp.StatusCode)
}
var out authorizeResponse
if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
return false, fmt.Errorf("rbac: antwort dekodieren: %w", err)
}
if !out.Allowed {
return false, ErrDenied
}
return true, nil
}
func (c *Client) httpClient() *http.Client {
if c.HTTPClient != nil {
return c.HTTPClient
}
return http.DefaultClient
}
+32 -30
View File
@@ -6,43 +6,45 @@
package retentionapi package retentionapi
import ( import (
"errors"
"net/http" "net/http"
"strings"
"gitea.perlbach24.de/scripte/nexarch/archive/internal/rbacclient"
) )
// requiredRoleHeader ist der Header-Name des PROVISORISCHEN Rollen- // requestedRoleHeader trägt die vom Aufrufer beanspruchte Rolle. Die
// Checks (siehe RequireRole-Dokumentation). // eigentliche Autorisierungsentscheidung trifft NICHT dieser Header,
const requiredRoleHeader = "X-Admin-Roles" // sondern Core RBAC-06 (siehe RequireRBAC) — der Header ist nur die
// Behauptung, RBAC-06 prüft sie gegen die echten Policy-Rules.
const requestedRoleHeader = "X-User-Role"
// RequireRole ist ein PROVISORISCHER Rollen-Check, KEIN RBAC-02-Aufruf. // RequireRBAC ist RET-08: die Ablösung des früheren, header-basierten
// // Provisoriums (siehe archive/docs/RET-06-API-PRUEFPROTOKOLL.md) durch
// Core RBAC-02 (internal/policy, Enforcer.Authorize) ist reiner // einen echten Aufruf von Core RBAC-06 (POST /authorize). Fail-closed:
// Go-Code innerhalb des Core-Moduls, hat keine HTTP-Schnittstelle, über // jeder Fehler beim Aufruf von RBAC-06 (Transport, Timeout, Ablehnung)
// die Archive (physisch getrenntes Go-Modul) es aufrufen könnte — // führt zur Verweigerung, niemals zu stillschweigendem Zugriff.
// derselbe "gefunden, aber nicht modulübergreifend verdrahtet"-Befund func RequireRBAC(client *rbacclient.Client, permission string, next http.HandlerFunc) http.HandlerFunc {
// wie bei Core FDN-03/FDN-09. Bis ein Core-seitiger HTTP-Wrapper um
// RBAC-02 existiert (eigenes, künftiges Core-Ticket, z. B. RBAC-06),
// prüft dieser Middleware NUR einen einfachen, selbst gesetzten Header
// (`X-Admin-Roles`, kommagetrennt) auf das Vorhandensein der
// geforderten Rolle — KEINE echte Autorisierung gegen Core, leicht zu
// fälschen von jedem, der den Header selbst setzen kann. Muss ersetzt
// werden, sobald der Core-HTTP-Wrapper existiert.
func RequireRole(requiredRole string, next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
roles := r.Header.Get(requiredRoleHeader) role := r.Header.Get(requestedRoleHeader)
if !containsRole(roles, requiredRole) { if role == "" {
http.Error(w, "zugriff verweigert: rolle "+requiredRole+" erforderlich (provisorischer check, siehe RequireRole)", http.StatusForbidden) http.Error(w, "zugriff verweigert: header "+requestedRoleHeader+" erforderlich", http.StatusForbidden)
return
}
allowed, err := client.Authorize(r.Context(), role, permission)
if err != nil {
if errors.Is(err, rbacclient.ErrDenied) {
http.Error(w, "zugriff verweigert: rolle "+role+" hat kein recht "+permission, http.StatusForbidden)
return
}
// RBAC-06 nicht erreichbar oder fehlerhafte Antwort:
// fail-closed, kein Fallback auf offenen Zugriff.
http.Error(w, "zugriff verweigert: autorisierungsdienst nicht erreichbar", http.StatusForbidden)
return
}
if !allowed {
http.Error(w, "zugriff verweigert: rolle "+role+" hat kein recht "+permission, http.StatusForbidden)
return return
} }
next(w, r) next(w, r)
} }
} }
func containsRole(commaSeparated, role string) bool {
for _, r := range strings.Split(commaSeparated, ",") {
if strings.TrimSpace(r) == role {
return true
}
}
return false
}
+77 -12
View File
@@ -1,22 +1,57 @@
package retentionapi package retentionapi
import ( import (
"encoding/json"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
"gitea.perlbach24.de/scripte/nexarch/archive/internal/rbacclient"
) )
// TestRequireRole_MissingRoleReturns403 ist die vom Nutzer geforderte const testServiceToken = "test-service-token"
// Negativpruefung fuer den provisorischen Rollen-Check: keine/falsche
// Rolle => 403, sonst waere der Check nicht pruefbar. // fakeRBACServer simuliert Core RBAC-06 (POST /authorize): erlaubt nur
func TestRequireRole_MissingRoleReturns403(t *testing.T) { // die genau angegebene role/permission-Kombination, verlangt den
handler := RequireRole(adminRole, func(w http.ResponseWriter, r *http.Request) { // Service-Token. Damit werden RequireRBAC-Aufrufe real über HTTP
// getestet, ohne echtes Core-Modul.
func fakeRBACServer(t *testing.T, allowRole, allowPermission string) *rbacclient.Client {
t.Helper()
mux := http.NewServeMux()
mux.HandleFunc("POST /authorize", func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("X-Service-Token") != testServiceToken {
http.Error(w, "kein service-token", http.StatusUnauthorized)
return
}
var req struct {
Role string `json:"role"`
Permission string `json:"permission"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
allowed := req.Role == allowRole && req.Permission == allowPermission
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]bool{"allowed": allowed})
})
server := httptest.NewServer(mux)
t.Cleanup(server.Close)
return rbacclient.New(server.URL, testServiceToken)
}
// TestRequireRBAC_DeniedRoleReturns403 ist die vom Nutzer geforderte
// Negativpruefung, jetzt gegen echte Policy-Rules ueber RBAC-06 (nicht
// mehr nur ein Header-Wert wie im frueheren Provisorium).
func TestRequireRBAC_DeniedRoleReturns403(t *testing.T) {
client := fakeRBACServer(t, "archive_admin", "retention.configure")
handler := RequireRBAC(client, "retention.configure", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
}) })
server := httptest.NewServer(handler) server := httptest.NewServer(handler)
defer server.Close() defer server.Close()
// Kein Header gesetzt. // Kein Rollen-Header.
resp, err := http.Get(server.URL) resp, err := http.Get(server.URL)
if err != nil { if err != nil {
t.Fatalf("get: %v", err) t.Fatalf("get: %v", err)
@@ -26,9 +61,9 @@ func TestRequireRole_MissingRoleReturns403(t *testing.T) {
t.Fatalf("ohne rollen-header: status = %d, want 403", resp.StatusCode) t.Fatalf("ohne rollen-header: status = %d, want 403", resp.StatusCode)
} }
// Falsche Rolle gesetzt. // Falsche Rolle - RBAC-06 lehnt real ab.
req, _ := http.NewRequest(http.MethodGet, server.URL, nil) req, _ := http.NewRequest(http.MethodGet, server.URL, nil)
req.Header.Set("X-Admin-Roles", "irgendwas_anderes") req.Header.Set(requestedRoleHeader, "irgendwas_anderes")
resp2, err := http.DefaultClient.Do(req) resp2, err := http.DefaultClient.Do(req)
if err != nil { if err != nil {
t.Fatalf("get: %v", err) t.Fatalf("get: %v", err)
@@ -39,16 +74,18 @@ func TestRequireRole_MissingRoleReturns403(t *testing.T) {
} }
} }
// TestRequireRole_CorrectRoleAllowsAccess ist der positive Gegentest. // TestRequireRBAC_AllowedRoleGrantsAccess ist der positive Gegentest,
func TestRequireRole_CorrectRoleAllowsAccess(t *testing.T) { // real gegen die fake-RBAC-06-Antwort geprueft.
handler := RequireRole(adminRole, func(w http.ResponseWriter, r *http.Request) { func TestRequireRBAC_AllowedRoleGrantsAccess(t *testing.T) {
client := fakeRBACServer(t, "archive_admin", "retention.configure")
handler := RequireRBAC(client, "retention.configure", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
}) })
server := httptest.NewServer(handler) server := httptest.NewServer(handler)
defer server.Close() defer server.Close()
req, _ := http.NewRequest(http.MethodGet, server.URL, nil) req, _ := http.NewRequest(http.MethodGet, server.URL, nil)
req.Header.Set("X-Admin-Roles", "irgendwas_anderes, archive_admin") req.Header.Set(requestedRoleHeader, "archive_admin")
resp, err := http.DefaultClient.Do(req) resp, err := http.DefaultClient.Do(req)
if err != nil { if err != nil {
t.Fatalf("get: %v", err) t.Fatalf("get: %v", err)
@@ -58,3 +95,31 @@ func TestRequireRole_CorrectRoleAllowsAccess(t *testing.T) {
t.Fatalf("mit korrekter rolle: status = %d, want 200", resp.StatusCode) t.Fatalf("mit korrekter rolle: status = %d, want 200", resp.StatusCode)
} }
} }
// TestRequireRBAC_UnreachableRBACFailsClosed ist die geforderte
// Pflichtpruefung: Ausfall von RBAC-06 fuehrt zur Ablehnung, kein
// Fallback auf offenen Zugriff.
func TestRequireRBAC_UnreachableRBACFailsClosed(t *testing.T) {
unreachable := rbacclient.New("http://127.0.0.1:1", testServiceToken)
called := false
handler := RequireRBAC(unreachable, "retention.configure", func(w http.ResponseWriter, r *http.Request) {
called = true
w.WriteHeader(http.StatusOK)
})
server := httptest.NewServer(handler)
defer server.Close()
req, _ := http.NewRequest(http.MethodGet, server.URL, nil)
req.Header.Set(requestedRoleHeader, "archive_admin")
resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Fatalf("get: %v", err)
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusForbidden {
t.Fatalf("bei nicht erreichbarem rbac-06: status = %d, want 403 (fail-closed)", resp.StatusCode)
}
if called {
t.Fatal("handler haette bei nicht erreichbarem rbac-06 nie erreicht werden duerfen")
}
}
+11 -8
View File
@@ -7,19 +7,22 @@ import (
"github.com/jackc/pgx/v5/pgxpool" "github.com/jackc/pgx/v5/pgxpool"
"gitea.perlbach24.de/scripte/nexarch/archive/internal/rbacclient"
"gitea.perlbach24.de/scripte/nexarch/archive/internal/retentionengine" "gitea.perlbach24.de/scripte/nexarch/archive/internal/retentionengine"
) )
const adminRole = "archive_admin" // retentionConfigurePermission ist das bei Core RBAC-02/RBAC-06
// geprüfte Recht für alle RET-06-API-Endpunkte.
const retentionConfigurePermission = "retention.configure"
// Mount registriert alle RET-06-API-Endpunkte auf mux, jeweils hinter // Mount registriert alle RET-06-API-Endpunkte auf mux, jeweils hinter
// dem provisorischen Rollen-Check (siehe authz.go) — Akzeptanzkriterium // RequireRBAC (RET-08, echter RBAC-06-Aufruf) — Akzeptanzkriterium 3:
// 3: Änderungen an Fristen sind nur berechtigten Rollen zugänglich. // Änderungen an Fristen sind nur berechtigten Rollen zugänglich.
func Mount(mux *http.ServeMux, pool *pgxpool.Pool) { func Mount(mux *http.ServeMux, pool *pgxpool.Pool, rbac *rbacclient.Client) {
mux.HandleFunc("POST /retention-classes", RequireRole(adminRole, configureHandler(pool))) mux.HandleFunc("POST /retention-classes", RequireRBAC(rbac, retentionConfigurePermission, configureHandler(pool)))
mux.HandleFunc("POST /retention-classes/{class}/deactivate", RequireRole(adminRole, deactivateHandler(pool))) mux.HandleFunc("POST /retention-classes/{class}/deactivate", RequireRBAC(rbac, retentionConfigurePermission, deactivateHandler(pool)))
mux.HandleFunc("GET /retention-classes", RequireRole(adminRole, listHandler(pool))) mux.HandleFunc("GET /retention-classes", RequireRBAC(rbac, retentionConfigurePermission, listHandler(pool)))
mux.HandleFunc("GET /retention-classes/preview", RequireRole(adminRole, previewHandler(pool))) mux.HandleFunc("GET /retention-classes/preview", RequireRBAC(rbac, retentionConfigurePermission, previewHandler(pool)))
} }
type configureRequest struct { type configureRequest struct {
@@ -67,7 +67,7 @@ func authedRequest(t *testing.T, method, url string, body []byte) *http.Response
if err != nil { if err != nil {
t.Fatalf("request erstellen: %v", err) t.Fatalf("request erstellen: %v", err)
} }
req.Header.Set("X-Admin-Roles", adminRole) req.Header.Set(requestedRoleHeader, "archive_admin")
resp, err := http.DefaultClient.Do(req) resp, err := http.DefaultClient.Do(req)
if err != nil { if err != nil {
t.Fatalf("request senden: %v", err) t.Fatalf("request senden: %v", err)
@@ -80,7 +80,7 @@ func authedRequest(t *testing.T, method, url string, body []byte) *http.Response
func TestConfigureAndListHandler_RealHTTPRoundTrip(t *testing.T) { func TestConfigureAndListHandler_RealHTTPRoundTrip(t *testing.T) {
pool := requireTestPool(t) pool := requireTestPool(t)
mux := http.NewServeMux() mux := http.NewServeMux()
Mount(mux, pool) Mount(mux, pool, fakeRBACServer(t, "archive_admin", retentionConfigurePermission))
server := httptest.NewServer(mux) server := httptest.NewServer(mux)
defer server.Close() defer server.Close()
@@ -110,7 +110,7 @@ func TestDeactivateHandler_RealHTTPRoundTrip(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
mux := http.NewServeMux() mux := http.NewServeMux()
Mount(mux, pool) Mount(mux, pool, fakeRBACServer(t, "archive_admin", retentionConfigurePermission))
server := httptest.NewServer(mux) server := httptest.NewServer(mux)
defer server.Close() defer server.Close()
@@ -149,7 +149,7 @@ func TestPreviewHandler_MatchesPeriodicJobResult(t *testing.T) {
} }
mux := http.NewServeMux() mux := http.NewServeMux()
Mount(mux, pool) Mount(mux, pool, fakeRBACServer(t, "archive_admin", retentionConfigurePermission))
server := httptest.NewServer(mux) server := httptest.NewServer(mux)
defer server.Close() defer server.Close()