mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-09-19 17:50:12 +00:00
110 lines
6.0 KiB
Go
110 lines
6.0 KiB
Go
//go:build agentcompat
|
|
|
|
package controller
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/nezhahq/nezha/model"
|
|
"github.com/nezhahq/nezha/service/rpc"
|
|
"github.com/nezhahq/nezha/service/singleton"
|
|
)
|
|
|
|
func TestAgentcompatNATCapabilityUsesExactCurrentProfilePermission(t *testing.T) {
|
|
cleanup, userID := setupMCPTest(t)
|
|
t.Cleanup(cleanup)
|
|
handler := rpc.NewNezhaHandler()
|
|
originalHandler := rpc.NezhaHandlerSingleton
|
|
originalNAT := singleton.NATShared
|
|
rpc.NezhaHandlerSingleton = handler
|
|
t.Cleanup(func() {
|
|
rpc.NezhaHandlerSingleton = originalHandler
|
|
singleton.NATShared = originalNAT
|
|
})
|
|
require.NoError(t, singleton.DB.AutoMigrate(&model.NAT{}))
|
|
secondServer := &model.Server{Common: model.Common{ID: 8}}
|
|
secondServer.SetUserID(userID)
|
|
singleton.ServerShared.InsertForTest(secondServer)
|
|
profile := &model.NAT{Common: model.Common{ID: 41, UserID: userID}, Name: "private-profile", ServerID: 7, Domain: "private-profile.example"}
|
|
foreignProfile := &model.NAT{Common: model.Common{ID: 42, UserID: 999}, Name: "foreign-profile", ServerID: 7, Domain: "foreign-profile.example"}
|
|
require.NoError(t, singleton.DB.Create(profile).Error)
|
|
require.NoError(t, singleton.DB.Create(foreignProfile).Error)
|
|
singleton.NATShared = singleton.NewNATClass()
|
|
token, plaintext := mkToken(t, userID, []string{model.ScopeNATRead}, []uint64{7, 8})
|
|
server := newAgentcompatCapabilityServer(t)
|
|
|
|
capability := registerAgentcompatCapability(t, server.URL, plaintext, agentcompatCapabilityRegisterRequest{Purpose: "nat", ServerID: 7, ResourceID: 41})
|
|
status, mismatchBody := postAgentcompatEmpty(t, server.URL+agentcompatCapabilityRegisterPath, plaintext, agentcompatCapabilityRegisterRequest{Purpose: "nat", ServerID: 8, ResourceID: 41})
|
|
require.Equal(t, http.StatusOK, status)
|
|
require.Contains(t, mismatchBody, errAgentcompatCapabilityUnavailable.Error())
|
|
foreignStatus, foreignBody := postAgentcompatEmpty(t, server.URL+agentcompatCapabilityRegisterPath, plaintext, agentcompatCapabilityRegisterRequest{Purpose: "nat", ServerID: 7, ResourceID: 42})
|
|
require.Equal(t, http.StatusOK, foreignStatus)
|
|
require.Contains(t, foreignBody, errAgentcompatCapabilityUnavailable.Error())
|
|
|
|
parsed, err := rpc.ParseAgentCompatIOStreamCapability(capability)
|
|
require.NoError(t, err)
|
|
access := rpc.AgentCompatCapabilityAccess{
|
|
Capability: parsed, Owner: rpc.AgentCompatCapabilityOwner{PATID: token.ID, UserID: userID},
|
|
Purpose: rpc.AgentCompatCapabilityNAT, TargetServerID: 7, ResourceID: 41, ServerAccessAllowed: true,
|
|
}
|
|
handle, err := handler.ConsumeAgentCompatNATCapability(access)
|
|
require.NoError(t, err)
|
|
lease, err := handler.CreateAgentCompatNATStream(handle, "private-nat-stream")
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() { require.NoError(t, handler.CloseAgentCompatNATStreamLease(lease)) })
|
|
require.NoError(t, handler.PublishAgentCompatNATStream(handle, rpc.AgentCompatNATPublication{Purpose: rpc.AgentCompatCapabilityNAT, TargetServerID: 7, ResourceID: 41, StreamID: "private-nat-stream"}))
|
|
|
|
profile.ServerID = 8
|
|
singleton.NATShared.Update(profile)
|
|
_, unavailableErr := postAgentcompatCapability[agentcompatCapabilityWaitRequest, agentcompatCapabilityWaitResponse](context.Background(), server.URL+agentcompatCapabilityWaitPath, plaintext, agentcompatCapabilityWaitRequest{Capability: capability, Purpose: "nat", ServerID: 7, ResourceID: 41})
|
|
require.Error(t, unavailableErr)
|
|
require.NotContains(t, unavailableErr.Error(), capability)
|
|
require.NotContains(t, unavailableErr.Error(), "private-nat-stream")
|
|
profile.ServerID = 7
|
|
singleton.NATShared.Update(profile)
|
|
waitContext, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
defer cancel()
|
|
waited, err := postAgentcompatCapability[agentcompatCapabilityWaitRequest, agentcompatCapabilityWaitResponse](waitContext, server.URL+agentcompatCapabilityWaitPath, plaintext, agentcompatCapabilityWaitRequest{Capability: capability, Purpose: "nat", ServerID: 7, ResourceID: 41})
|
|
require.NoError(t, err)
|
|
require.Equal(t, "private-nat-stream", waited.StreamID)
|
|
}
|
|
|
|
func TestAgentcompatCapabilityOwnerIncludesCurrentAdminRole(t *testing.T) {
|
|
cleanup, _ := setupMCPTest(t)
|
|
t.Cleanup(cleanup)
|
|
handler := rpc.NewNezhaHandler()
|
|
originalHandler := rpc.NezhaHandlerSingleton
|
|
rpc.NezhaHandlerSingleton = handler
|
|
t.Cleanup(func() { rpc.NezhaHandlerSingleton = originalHandler })
|
|
admin := &model.User{Common: model.Common{ID: 300}, Username: "cap-admin", Role: model.RoleAdmin}
|
|
require.NoError(t, singleton.DB.Create(admin).Error)
|
|
target := &model.Server{Common: model.Common{ID: 8}}
|
|
target.SetUserID(999)
|
|
singleton.ServerShared.InsertForTest(target)
|
|
token, plaintext := mkDistinctCapabilityToken(t, admin.ID, "admin")
|
|
token.SetServerIDs([]uint64{8})
|
|
require.NoError(t, singleton.DB.Model(token).Update("servers_csv", token.ServersCSV).Error)
|
|
server := newAgentcompatCapabilityServer(t)
|
|
capability := registerAgentcompatCapability(t, server.URL, plaintext, agentcompatCapabilityRegisterRequest{Purpose: "terminal", ServerID: 8})
|
|
parsed, err := rpc.ParseAgentCompatIOStreamCapability(capability)
|
|
require.NoError(t, err)
|
|
require.NoError(t, handler.CreateStreamWithPurpose("admin-private-stream", admin.ID, 8, rpc.PurposeTerminal))
|
|
require.NoError(t, handler.BindAgentCompatIOStreamCapability(rpc.AgentCompatCapabilityBinding{
|
|
AgentCompatCapabilityAccess: rpc.AgentCompatCapabilityAccess{
|
|
Capability: parsed, Owner: rpc.AgentCompatCapabilityOwner{PATID: token.ID, UserID: admin.ID, IsAdmin: true},
|
|
Purpose: rpc.AgentCompatCapabilityTerminal, TargetServerID: 8, ServerAccessAllowed: true,
|
|
},
|
|
StreamID: "admin-private-stream",
|
|
}))
|
|
waitContext, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
defer cancel()
|
|
waited, err := postAgentcompatCapability[agentcompatCapabilityWaitRequest, agentcompatCapabilityWaitResponse](waitContext, server.URL+agentcompatCapabilityWaitPath, plaintext, agentcompatCapabilityWaitRequest{Capability: capability, Purpose: "terminal", ServerID: 8})
|
|
require.NoError(t, err)
|
|
require.Equal(t, "admin-private-stream", waited.StreamID)
|
|
}
|