af2366acb5
Fase 5 del issue 0010 — RBAC y middlewares de auth.
- rbac_check es pura: solo recorre la matriz roles/permisos
- jwt_middleware extrae token del header Authorization: Bearer, valida e
inyecta claims en el context con una key privada (jwtCtxKey struct{})
- rbac_middleware requiere jwt_middleware antes; lee role de claims.Custom
- Helper JWTClaimsFromContext para acceder a las claims desde handlers
- 401 claro si RBAC se usa sin JWT antes (code: no_claims)
99 lines
2.8 KiB
Go
99 lines
2.8 KiB
Go
package infra
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func buildRBACRoles() []Role {
|
|
return []Role{
|
|
{Name: "admin", Permissions: []Permission{{Resource: "users", Action: "delete"}}},
|
|
{Name: "viewer", Permissions: []Permission{{Resource: "users", Action: "read"}}},
|
|
}
|
|
}
|
|
|
|
func buildAuthedRequest(t *testing.T, role, secret string) *http.Request {
|
|
t.Helper()
|
|
claims := JWTClaims{
|
|
Subject: "user-1",
|
|
ExpiresAt: time.Now().Add(time.Hour).Unix(),
|
|
Custom: map[string]any{"role": role},
|
|
}
|
|
tok, err := JWTGenerate(claims, secret)
|
|
if err != nil {
|
|
t.Fatalf("JWTGenerate: %v", err)
|
|
}
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
req.Header.Set("Authorization", "Bearer "+tok)
|
|
return req
|
|
}
|
|
|
|
func TestRBACMiddleware_PermissionGranted(t *testing.T) {
|
|
secret := "s"
|
|
chain := HTTPMiddlewareChain(
|
|
JWTMiddleware(secret),
|
|
RBACMiddleware(buildRBACRoles(), Permission{Resource: "users", Action: "delete"}),
|
|
)
|
|
var called bool
|
|
handler := chain(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
called = true
|
|
w.WriteHeader(200)
|
|
}))
|
|
|
|
rec := httptest.NewRecorder()
|
|
handler.ServeHTTP(rec, buildAuthedRequest(t, "admin", secret))
|
|
if rec.Code != 200 || !called {
|
|
t.Errorf("admin deberia pasar: status=%d called=%v", rec.Code, called)
|
|
}
|
|
}
|
|
|
|
func TestRBACMiddleware_PermissionDenied(t *testing.T) {
|
|
secret := "s"
|
|
chain := HTTPMiddlewareChain(
|
|
JWTMiddleware(secret),
|
|
RBACMiddleware(buildRBACRoles(), Permission{Resource: "users", Action: "delete"}),
|
|
)
|
|
handler := chain(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
t.Error("viewer no deberia pasar")
|
|
}))
|
|
|
|
rec := httptest.NewRecorder()
|
|
handler.ServeHTTP(rec, buildAuthedRequest(t, "viewer", secret))
|
|
if rec.Code != 403 {
|
|
t.Errorf("status = %d, esperaba 403", rec.Code)
|
|
}
|
|
}
|
|
|
|
func TestRBACMiddleware_UnknownRole(t *testing.T) {
|
|
secret := "s"
|
|
chain := HTTPMiddlewareChain(
|
|
JWTMiddleware(secret),
|
|
RBACMiddleware(buildRBACRoles(), Permission{Resource: "users", Action: "read"}),
|
|
)
|
|
handler := chain(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
t.Error("rol ghost no deberia pasar")
|
|
}))
|
|
|
|
rec := httptest.NewRecorder()
|
|
handler.ServeHTTP(rec, buildAuthedRequest(t, "ghost", secret))
|
|
if rec.Code != 403 {
|
|
t.Errorf("status = %d, esperaba 403", rec.Code)
|
|
}
|
|
}
|
|
|
|
func TestRBACMiddleware_NoClaims(t *testing.T) {
|
|
// Sin JWTMiddleware delante → no hay claims en context
|
|
mw := RBACMiddleware(buildRBACRoles(), Permission{Resource: "users", Action: "read"})
|
|
handler := mw(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
t.Error("no deberia ejecutarse")
|
|
}))
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
rec := httptest.NewRecorder()
|
|
handler.ServeHTTP(rec, req)
|
|
if rec.Code != 401 {
|
|
t.Errorf("status = %d, esperaba 401", rec.Code)
|
|
}
|
|
}
|