Files
nezha_domains/service/rpc/auth_test.go
T
2026-05-19 01:58:53 +00:00

102 lines
3.1 KiB
Go

package rpc
import (
"testing"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"github.com/nezhahq/nezha/model"
"github.com/nezhahq/nezha/service/singleton"
)
// setupAuthAgentFixture seeds an in-memory DB and ServerShared with two
// servers belonging to different users so we can assert that a secret bound
// to user A cannot resolve a server UUID owned by user B.
func setupAuthAgentFixture(t *testing.T) func() {
t.Helper()
originalDB := singleton.DB
originalServerShared := singleton.ServerShared
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
if err != nil {
t.Fatalf("open db: %v", err)
}
if err := db.AutoMigrate(&model.Server{}); err != nil {
t.Fatalf("migrate: %v", err)
}
if err := db.Create(&model.Server{
Common: model.Common{ID: 1, UserID: 100},
UUID: "uuid-alice",
Name: "alice-srv",
}).Error; err != nil {
t.Fatalf("create alice: %v", err)
}
if err := db.Create(&model.Server{
Common: model.Common{ID: 2, UserID: 200},
UUID: "uuid-bob",
Name: "bob-srv",
}).Error; err != nil {
t.Fatalf("create bob: %v", err)
}
singleton.DB = db
singleton.ServerShared = singleton.NewServerClass()
return func() {
singleton.DB = originalDB
singleton.ServerShared = originalServerShared
}
}
func TestAuthorizeAgentForUUIDAcceptsOwnedServer(t *testing.T) {
defer setupAuthAgentFixture(t)()
cid, hasID, err := authorizeAgentForUUID(100, "uuid-alice")
if err != nil {
t.Fatalf("alice with her own server UUID must not error, got %v", err)
}
if !hasID || cid != 1 {
t.Fatalf("expected (cid=1, hasID=true), got (cid=%d, hasID=%v)", cid, hasID)
}
}
// Core regression: an agent presenting user A's secret but user B's server
// UUID must be rejected. Previously the code returned the resolved server ID
// without verifying the UserID matched the secret owner, allowing same-tenant
// (and worse — cross-tenant if UUID leaks) server impersonation.
func TestAuthorizeAgentForUUIDRejectsForeignServerUUID(t *testing.T) {
defer setupAuthAgentFixture(t)()
_, _, err := authorizeAgentForUUID(100, "uuid-bob") // alice's secret + bob's UUID
if err == nil {
t.Fatalf("UUID owned by another user must be rejected")
}
}
func TestAuthorizeAgentForUUIDAllowsGlobalDefaultSecret(t *testing.T) {
defer setupAuthAgentFixture(t)()
cid, hasID, err := authorizeAgentForUUID(0, "uuid-bob")
if err != nil {
t.Fatalf("global default secret must be allowed to use existing UUIDs, got %v", err)
}
if !hasID || cid != 2 {
t.Fatalf("expected (cid=2, hasID=true), got (cid=%d, hasID=%v)", cid, hasID)
}
}
// An unknown UUID must NOT be treated as an impersonation attempt — it is
// the normal first-time registration path and the caller (Check) creates a
// new server bound to the secret owner.
func TestAuthorizeAgentForUUIDPermitsUnknownUUIDForRegistration(t *testing.T) {
defer setupAuthAgentFixture(t)()
cid, hasID, err := authorizeAgentForUUID(100, "uuid-never-seen-before")
if err != nil {
t.Fatalf("unknown UUID must be permitted for new registration, got %v", err)
}
if hasID {
t.Fatalf("hasID must be false for unknown UUID, got cid=%d", cid)
}
}