178 lines
5.1 KiB
Go
178 lines
5.1 KiB
Go
package auth
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestUserStore(t *testing.T) {
|
|
store, _ := NewUserStore("")
|
|
// 正式运行不再内置种子账号,用户需显式创建。
|
|
if _, ok := store.Get("admin"); ok {
|
|
t.Fatal("no seed admin should exist")
|
|
}
|
|
if _, err := store.Create("admin", "Admin123!", "admin"); err != nil {
|
|
t.Fatalf("create admin: %v", err)
|
|
}
|
|
if _, ok := store.Get("admin"); !ok {
|
|
t.Fatal("admin should exist after create")
|
|
}
|
|
_, ok := store.Verify("admin", "Admin123!")
|
|
if !ok {
|
|
t.Fatal("admin password should verify")
|
|
}
|
|
if _, ok := store.Verify("admin", "wrong"); ok {
|
|
t.Fatal("wrong password should fail")
|
|
}
|
|
if _, err := store.Create("bob", "secret123", "viewer"); err != nil {
|
|
t.Fatalf("create bob: %v", err)
|
|
}
|
|
if _, err := store.Create("bob", "secret123", "viewer"); err == nil {
|
|
t.Fatal("duplicate should fail")
|
|
}
|
|
}
|
|
|
|
func TestJWT(t *testing.T) {
|
|
mgr := NewJWTManager("test-secret", time.Hour)
|
|
tok, err := mgr.Sign("alice", "admin")
|
|
if err != nil {
|
|
t.Fatalf("sign: %v", err)
|
|
}
|
|
claims, err := mgr.Verify(tok)
|
|
if err != nil {
|
|
t.Fatalf("verify: %v", err)
|
|
}
|
|
if claims.Username != "alice" || claims.Role != "admin" {
|
|
t.Fatalf("claims mismatch: %+v", claims)
|
|
}
|
|
if _, err := mgr.Verify(tok + "x"); err == nil {
|
|
t.Fatal("tampered should fail")
|
|
}
|
|
mgr2 := NewJWTManager("test-secret", -time.Hour)
|
|
tok2, _ := mgr2.Sign("alice", "admin")
|
|
if _, err := mgr2.Verify(tok2); err == nil {
|
|
t.Fatal("expired should fail")
|
|
}
|
|
}
|
|
|
|
func TestCasbinEnforcer(t *testing.T) {
|
|
tmpPol := os.TempDir() + "/test_policy.csv"
|
|
data, _ := os.ReadFile("policy.csv")
|
|
_ = os.WriteFile(tmpPol, data, 0644)
|
|
defer os.Remove(tmpPol)
|
|
|
|
store, _ := NewUserStore("")
|
|
_, _ = store.Create("alice", "pass12345", "viewer")
|
|
_, _ = store.Create("pub", "pass12345", "publisher")
|
|
enc, err := NewEnforcer("model.conf", tmpPol, store)
|
|
if err != nil {
|
|
t.Fatalf("new enforcer: %v", err)
|
|
}
|
|
ok, _ := enc.Enforce("admin", "room", "publish")
|
|
if !ok {
|
|
t.Fatal("admin should can publish")
|
|
}
|
|
ok, _ = enc.Enforce("viewer", "room", "publish")
|
|
if ok {
|
|
t.Fatal("viewer should not publish")
|
|
}
|
|
ok, _ = enc.Enforce("viewer", "room", "subscribe")
|
|
if !ok {
|
|
t.Fatal("viewer should can subscribe")
|
|
}
|
|
ok, _ = enc.Enforce("publisher", "room", "publish")
|
|
if !ok {
|
|
t.Fatal("publisher should can publish")
|
|
}
|
|
ok, _ = enc.Enforce("guest", "room", "watch")
|
|
if ok {
|
|
t.Fatal("guest should not watch")
|
|
}
|
|
ok, _ = enc.Enforce("alice", "room", "subscribe")
|
|
if !ok {
|
|
t.Fatal("alice (viewer) should can subscribe")
|
|
}
|
|
ok, _ = enc.Enforce("alice", "room", "publish")
|
|
if ok {
|
|
t.Fatal("alice (viewer) should not publish")
|
|
}
|
|
}
|
|
|
|
func TestAuthManagerLogin(t *testing.T) {
|
|
tmpPol := os.TempDir() + "/test_policy2.csv"
|
|
data, _ := os.ReadFile("policy.csv")
|
|
_ = os.WriteFile(tmpPol, data, 0644)
|
|
defer os.Remove(tmpPol)
|
|
|
|
store, _ := NewUserStore("")
|
|
_, _ = store.Create("admin", "Admin123!", "admin")
|
|
enc, _ := NewEnforcer("model.conf", tmpPol, store)
|
|
mgr := NewManagerWithStore(store, "test-jwt", time.Hour, enc)
|
|
|
|
tok, user, err := mgr.Login("admin", "Admin123!")
|
|
if err != nil {
|
|
t.Fatalf("login admin: %v", err)
|
|
}
|
|
if tok == "" || user.Username != "admin" {
|
|
t.Fatalf("login result bad: %v %v", tok, user)
|
|
}
|
|
if _, _, err := mgr.Login("admin", "wrong"); err == nil {
|
|
t.Fatal("wrong pwd should fail")
|
|
}
|
|
tok2, u2, err := mgr.Register("newuser", "pass12345", "viewer")
|
|
if err != nil {
|
|
t.Fatalf("register: %v", err)
|
|
}
|
|
if tok2 == "" || u2.Role != "viewer" {
|
|
t.Fatalf("register result bad")
|
|
}
|
|
claims, _, err := mgr.VerifyToken(tok)
|
|
if err != nil || claims.Username != "admin" {
|
|
t.Fatalf("verify token: %v %+v", err, claims)
|
|
}
|
|
}
|
|
|
|
func TestHTTPMiddleware(t *testing.T) {
|
|
tmpPol := os.TempDir() + "/test_policy3.csv"
|
|
data, _ := os.ReadFile("policy.csv")
|
|
_ = os.WriteFile(tmpPol, data, 0644)
|
|
defer os.Remove(tmpPol)
|
|
|
|
store, _ := NewUserStore("")
|
|
_, _ = store.Create("viewer", "Viewer123!", "viewer")
|
|
_, _ = store.Create("publisher", "Publisher123!", "publisher")
|
|
enc, _ := NewEnforcer("model.conf", tmpPol, store)
|
|
mgr := NewManagerWithStore(store, "test-jwt", time.Hour, enc)
|
|
|
|
protected := mgr.AuthorizeMiddleware("room", "publish", true)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Write([]byte("ok"))
|
|
}))
|
|
|
|
req := httptest.NewRequest("GET", "/", nil)
|
|
rec := httptest.NewRecorder()
|
|
protected.ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusUnauthorized {
|
|
t.Fatalf("expected 401, got %d", rec.Code)
|
|
}
|
|
tokViewer, _, _ := mgr.Login("viewer", "Viewer123!")
|
|
req2 := httptest.NewRequest("GET", "/", nil)
|
|
req2.Header.Set("Authorization", "Bearer "+tokViewer)
|
|
rec2 := httptest.NewRecorder()
|
|
protected.ServeHTTP(rec2, req2)
|
|
if rec2.Code != http.StatusForbidden {
|
|
t.Fatalf("viewer should be forbidden, got %d body=%s", rec2.Code, rec2.Body.String())
|
|
}
|
|
tokPub, _, _ := mgr.Login("publisher", "Publisher123!")
|
|
req3 := httptest.NewRequest("GET", "/", nil)
|
|
req3.Header.Set("Authorization", "Bearer "+tokPub)
|
|
rec3 := httptest.NewRecorder()
|
|
protected.ServeHTTP(rec3, req3)
|
|
if rec3.Code != http.StatusOK {
|
|
t.Fatalf("publisher should ok, got %d body=%s", rec3.Code, rec3.Body.String())
|
|
}
|
|
}
|
|
|