Merge pull request #171 from ssb-ngi-pointer/add-endpoint-tests

Add tunnel.endpoint tests and bugfix
This commit is contained in:
Henry 2021-04-21 11:49:43 +02:00 committed by GitHub
commit c28a3a59ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 289 additions and 89 deletions

2
go.mod
View File

@ -29,7 +29,7 @@ require (
github.com/volatiletech/sqlboiler-sqlite3 v0.0.0-20210314195744-a1c697a68aef // indirect
github.com/volatiletech/sqlboiler/v4 v4.5.0
github.com/volatiletech/strmangle v0.0.1
go.cryptoscope.co/muxrpc/v2 v2.0.1-0.20210414085324-530a7df6ea1c
go.cryptoscope.co/muxrpc/v2 v2.0.1-0.20210420101321-264bf6a7df2c
go.cryptoscope.co/netwrap v0.1.1
go.cryptoscope.co/secretstream v1.2.2
go.mindeco.de v1.11.0

2
go.sum
View File

@ -495,6 +495,8 @@ go.cryptoscope.co/muxrpc/v2 v2.0.1-0.20210414084152-08e3e6acc50d h1:PYOUNvgxKOeJ
go.cryptoscope.co/muxrpc/v2 v2.0.1-0.20210414084152-08e3e6acc50d/go.mod h1:MgaeojIkWY3lLuoNw1mlMT3b3jiZwOj/fgsoGZp/VNA=
go.cryptoscope.co/muxrpc/v2 v2.0.1-0.20210414085324-530a7df6ea1c h1:UQ2PCZ2VDKwvuWXtvyLkCXfdztJPr8tGhTnjsK36JGI=
go.cryptoscope.co/muxrpc/v2 v2.0.1-0.20210414085324-530a7df6ea1c/go.mod h1:MgaeojIkWY3lLuoNw1mlMT3b3jiZwOj/fgsoGZp/VNA=
go.cryptoscope.co/muxrpc/v2 v2.0.1-0.20210420101321-264bf6a7df2c h1:+78PDKICrSKwcsszzzLAO+kDiC46eVbgQfLivnBk5Wo=
go.cryptoscope.co/muxrpc/v2 v2.0.1-0.20210420101321-264bf6a7df2c/go.mod h1:MgaeojIkWY3lLuoNw1mlMT3b3jiZwOj/fgsoGZp/VNA=
go.cryptoscope.co/muxrpc/v2 v2.0.1 h1:U1dS1dsFk7/LygH8o2qsWy8dHB5g3YeLtm0lsN7c1CI=
go.cryptoscope.co/muxrpc/v2 v2.0.1/go.mod h1:MgaeojIkWY3lLuoNw1mlMT3b3jiZwOj/fgsoGZp/VNA=
go.cryptoscope.co/netwrap v0.1.0/go.mod h1:7zcYswCa4CT+ct54e9uH9+IIbYYETEMHKDNpzl8Ukew=

View File

@ -48,7 +48,7 @@ func (h *Handler) announce(_ context.Context, req *muxrpc.Request) (interface{},
h.state.AddEndpoint(*ref, req.Endpoint())
return false, nil
return true, nil
}
func (h *Handler) leave(_ context.Context, req *muxrpc.Request) (interface{}, error) {
@ -59,7 +59,7 @@ func (h *Handler) leave(_ context.Context, req *muxrpc.Request) (interface{}, er
h.state.Remove(*ref)
return false, nil
return true, nil
}
func (h *Handler) endpoints(ctx context.Context, req *muxrpc.Request, snk *muxrpc.ByteSink) error {
@ -70,7 +70,8 @@ func (h *Handler) endpoints(ctx context.Context, req *muxrpc.Request, snk *muxrp
// for future updates
h.state.Register(toPeer)
ref, err := network.GetFeedRefFromAddr(req.RemoteAddr())
// get public key from the calling peer
peer, err := network.GetFeedRefFromAddr(req.RemoteAddr())
if err != nil {
return err
}
@ -84,17 +85,18 @@ func (h *Handler) endpoints(ctx context.Context, req *muxrpc.Request, snk *muxrp
case roomdb.ModeCommunity:
fallthrough
case roomdb.ModeRestricted:
_, err := h.members.GetByFeed(ctx, *ref)
_, err := h.members.GetByFeed(ctx, *peer)
if err != nil {
return fmt.Errorf("external user are not allowed to enumerate members")
}
}
has := h.state.AlreadyAdded(*ref, req.Endpoint())
if !has {
// just send the current state to the new peer
toPeer.Update(h.state.List())
}
// add the peer to the room state if they arent already
h.state.AlreadyAdded(*peer, req.Endpoint())
// update the peer with
toPeer.Update(h.state.List())
return nil
}

View File

@ -13,7 +13,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.cryptoscope.co/muxrpc/v2"
"golang.org/x/sync/errgroup"
"github.com/ssb-ngi-pointer/go-ssb-room/internal/aliases"
"github.com/ssb-ngi-pointer/go-ssb-room/internal/maybemod/keys"
@ -26,8 +25,6 @@ import (
func TestAliasRegister(t *testing.T) {
testInit(t)
ctx, cancel := context.WithCancel(context.Background())
botgroup, ctx := errgroup.WithContext(ctx)
bs := newBotServer(ctx, mainLog)
r := require.New(t)
a := assert.New(t)
@ -41,55 +38,45 @@ func TestAliasRegister(t *testing.T) {
roomsrv.WithContext(ctx),
}
theBots := []*roomsrv.Server{}
theBots := []*testSession{}
srvMembers, serv := makeNamedTestBot(t, "srv", netOpts)
botgroup.Go(bs.Serve(serv))
theBots = append(theBots, serv)
session := makeNamedTestBot(t, "srv", ctx, netOpts)
theBots = append(theBots, session)
// we need bobs key to create the signature
bobsKey, err := keys.NewKeyPair(nil)
r.NoError(err)
bobsMembers, bob := makeNamedTestBot(t, "bob", append(netOpts,
bobSession := makeNamedTestBot(t, "bob", ctx, append(netOpts,
roomsrv.WithKeyPair(bobsKey),
))
botgroup.Go(bs.Serve(bob))
theBots = append(theBots, bob)
t.Cleanup(func() {
for _, bot := range theBots {
bot.Shutdown()
r.NoError(bot.Close())
}
r.NoError(botgroup.Wait())
})
theBots = append(theBots, bobSession)
// adds
_, err = srvMembers.Add(ctx, bob.Whoami(), roomdb.RoleMember)
_, err = session.srv.Members.Add(ctx, bobSession.srv.Whoami(), roomdb.RoleMember)
r.NoError(err)
// allow bots to dial the remote
// side-effect of re-using a room-server as the client
_, err = bobsMembers.Add(ctx, serv.Whoami(), roomdb.RoleMember)
_, err = bobSession.srv.Members.Add(ctx, session.srv.Whoami(), roomdb.RoleMember)
r.NoError(err)
// should work (we allowed A)
err = bob.Network.Connect(ctx, serv.Network.GetListenAddr())
err = bobSession.srv.Network.Connect(ctx, session.srv.Network.GetListenAddr())
r.NoError(err, "connect A to the Server")
t.Log("letting handshaking settle..")
time.Sleep(1 * time.Second)
clientForServer, ok := bob.Network.GetEndpointFor(serv.Whoami())
clientForServer, ok := bobSession.srv.Network.GetEndpointFor(session.srv.Whoami())
r.True(ok)
t.Log("got endpoint")
var testReg aliases.Registration
testReg.Alias = "bob"
testReg.RoomID = serv.Whoami()
testReg.UserID = bob.Whoami()
testReg.RoomID = session.srv.Whoami()
testReg.UserID = bobSession.srv.Whoami()
confirmation := testReg.Sign(bobsKey.Pair.Secret)
t.Logf("signature created: %x...", confirmation.Signature[:16])
@ -109,7 +96,7 @@ func TestAliasRegister(t *testing.T) {
a.Equal("", resolveURL.Path)
// server should have the alias now
alias, err := serv.Aliases.Resolve(ctx, "bob")
alias, err := session.srv.Aliases.Resolve(ctx, "bob")
r.NoError(err)
a.Equal(confirmation.Alias, alias.Name)
@ -118,5 +105,10 @@ func TestAliasRegister(t *testing.T) {
t.Log("alias stored")
for _, bot := range theBots {
bot.srv.Shutdown()
r.NoError(bot.srv.Close())
r.NoError(bot.serveGroup.Wait())
}
cancel()
}

View File

@ -32,21 +32,21 @@ func TestConnEstablishmentDeniedKey(t *testing.T) {
indexB
)
serv := theBots[indexSrv].Server
botA := theBots[indexA].Server
botB := theBots[indexB].Server
serv := theBots[indexSrv].srv
botA := theBots[indexA].srv
botB := theBots[indexB].srv
// allow A, deny B
theBots[indexSrv].Members.Add(ctx, botA.Whoami(), roomdb.RoleMember)
theBots[indexSrv].srv.Members.Add(ctx, botA.Whoami(), roomdb.RoleMember)
// since we want to verify denied keys in particular, let us both:
// a) add B as a member
theBots[indexSrv].Members.Add(ctx, botB.Whoami(), roomdb.RoleMember)
theBots[indexSrv].srv.Members.Add(ctx, botB.Whoami(), roomdb.RoleMember)
// b) ban B by adding them to the DeniedKeys database
theBots[indexSrv].Server.DeniedKeys.Add(ctx, botB.Whoami(), "rude")
theBots[indexSrv].srv.DeniedKeys.Add(ctx, botB.Whoami(), "rude")
// hack: allow bots to dial the server
theBots[indexA].Members.Add(ctx, serv.Whoami(), roomdb.RoleMember)
theBots[indexB].Members.Add(ctx, serv.Whoami(), roomdb.RoleMember)
theBots[indexA].srv.Members.Add(ctx, serv.Whoami(), roomdb.RoleMember)
theBots[indexB].srv.Members.Add(ctx, serv.Whoami(), roomdb.RoleMember)
// dial up B->A and C->A
// should work (we allowed A)

View File

@ -0,0 +1,197 @@
package go_test
import (
"context"
"encoding/base64"
"encoding/json"
"os"
"path/filepath"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.cryptoscope.co/muxrpc/v2"
"go.cryptoscope.co/muxrpc/v2/debug"
"go.cryptoscope.co/netwrap"
"go.cryptoscope.co/secretstream"
"github.com/ssb-ngi-pointer/go-ssb-room/internal/maybemod/keys"
"github.com/ssb-ngi-pointer/go-ssb-room/roomdb"
refs "go.mindeco.de/ssb-refs"
)
type announcements map[string]struct{}
// we will let three clients (alf, bre, crl) join and see that the endpoint output is as expected
func TestEndpointClients(t *testing.T) {
r := require.New(t)
a := assert.New(t)
testPath := filepath.Join("testrun", t.Name())
os.RemoveAll(testPath)
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
t.Cleanup(cancel)
// create the roomsrv
ts := makeNamedTestBot(t, "server", ctx, nil)
ctx = ts.ctx
// create three test clients
alf, alfFeed := ts.makeTestClient("alf")
bre, breFeed := ts.makeTestClient("bre")
carl, carlFeed := ts.makeTestClient("carl")
// let carl join the room
// carl wont announce to emulate manyverse
carlEndpointsSerc, err := carl.Source(ctx, muxrpc.TypeJSON, muxrpc.Method{"tunnel", "endpoints"})
r.NoError(err)
t.Log("carl opened endpoints")
announcementsForCarl := make(announcements)
go logStream(ts, carlEndpointsSerc, "carl", announcementsForCarl)
time.Sleep(1 * time.Second) // give some time to process new events
_, seen := announcementsForCarl[carlFeed.Ref()]
a.True(seen, "carl saw himself")
// let alf join the room
alfEndpointsSerc, err := alf.Source(ctx, muxrpc.TypeJSON, muxrpc.Method{"tunnel", "endpoints"})
r.NoError(err)
announcementsForAlf := make(announcements)
go logStream(ts, alfEndpointsSerc, "alf", announcementsForAlf)
time.Sleep(1 * time.Second) // give some time to process new events
// assert what alf saw
_, seen = announcementsForAlf[carlFeed.Ref()]
a.True(seen, "alf saw carl")
_, seen = announcementsForAlf[alfFeed.Ref()]
a.True(seen, "alf saw himself")
// assert what carl saw
_, seen = announcementsForCarl[alfFeed.Ref()]
a.True(seen, "carl saw alf")
// let bre join the room
breEndpointsSrc, err := bre.Source(ctx, muxrpc.TypeJSON, muxrpc.Method{"tunnel", "endpoints"})
r.NoError(err)
announcementsForBre := make(announcements)
go logStream(ts, breEndpointsSrc, "bre", announcementsForBre)
time.Sleep(1 * time.Second) // give some time to process new events
// assert bre saw the other two and herself
_, seen = announcementsForBre[carlFeed.Ref()]
a.True(seen, "bre saw carl")
_, seen = announcementsForBre[alfFeed.Ref()]
a.True(seen, "bre saw alf")
_, seen = announcementsForBre[breFeed.Ref()]
a.True(seen, "bre saw herself")
// assert the others saw bre
_, seen = announcementsForAlf[breFeed.Ref()]
a.True(seen, "alf saw bre")
_, seen = announcementsForCarl[breFeed.Ref()]
a.True(seen, "carl saw bre")
// terminate server and the clients
ts.srv.Shutdown()
alf.Terminate()
bre.Terminate()
carl.Terminate()
ts.srv.Close()
// wait for all muxrpc serve()s to exit
r.NoError(ts.serveGroup.Wait())
cancel()
}
func logStream(ts *testSession, src *muxrpc.ByteSource, who string, a announcements) {
var edps []refs.FeedRef
for src.Next(ts.ctx) {
body, err := src.Bytes()
if err != nil {
panic(err)
}
// ts.t.Log(who, "got body:", string(body))
err = json.Unmarshal(body, &edps)
if err != nil {
panic(err)
}
ts.t.Log(who, "got endpoints:", len(edps))
for i, f := range edps {
ts.t.Log(who, ":", i, f.ShortRef())
// mark as f is present
a[f.Ref()] = struct{}{}
}
}
if err := src.Err(); err != nil {
ts.t.Log("source errored: ", err)
return
}
ts.t.Log(who, "stream closed")
}
func (ts *testSession) makeTestClient(name string) (muxrpc.Endpoint, refs.FeedRef) {
r := require.New(ts.t)
// create a fresh keypairs for the clients
client, err := keys.NewKeyPair(nil)
r.NoError(err)
ts.t.Log(name, "is", client.Feed.ShortRef())
// add it as a memeber
memberID, err := ts.srv.Members.Add(ts.ctx, client.Feed, roomdb.RoleMember)
r.NoError(err)
ts.t.Log(name, "is member ID:", memberID)
// default app key for the secret-handshake connection
ak, err := base64.StdEncoding.DecodeString("1KHLiKZvAvjbY1ziZEHMXawbCEIM6qwjCDm3VYRan/s=")
r.NoError(err)
// create a shs client to authenticate and encrypt the connection
clientSHS, err := secretstream.NewClient(client.Pair, ak)
r.NoError(err)
// returns a new connection that went through shs and does boxstream
tcpAddr := netwrap.GetAddr(ts.srv.Network.GetListenAddr(), "tcp")
authedConn, err := netwrap.Dial(tcpAddr, clientSHS.ConnWrapper(ts.srv.Whoami().PubKey()))
r.NoError(err)
var muxMock muxrpc.FakeHandler
testPath := filepath.Join("testrun", ts.t.Name())
debugConn := debug.Dump(filepath.Join(testPath, "client-"+name), authedConn)
pkr := muxrpc.NewPacker(debugConn)
wsEndpoint := muxrpc.Handle(pkr, &muxMock, muxrpc.WithContext(ts.ctx))
srv := wsEndpoint.(muxrpc.Server)
ts.serveGroup.Go(func() error {
err = srv.Serve()
if err != nil {
ts.t.Logf("mux server %s error: %v", name, err)
}
return err
})
// check we are talking to a room
var yup bool
err = wsEndpoint.Async(ts.ctx, &yup, muxrpc.TypeJSON, muxrpc.Method{"tunnel", "isRoom"})
r.NoError(err)
r.True(yup, "server is not a room?")
return wsEndpoint, client.Feed
}

View File

@ -29,16 +29,16 @@ func TestTunnelServerSimple(t *testing.T) {
indexB
)
serv := theBots[indexSrv].Server
botA := theBots[indexA].Server
botB := theBots[indexB].Server
serv := theBots[indexSrv].srv
botA := theBots[indexA].srv
botB := theBots[indexB].srv
// only allow A
theBots[indexSrv].Members.Add(ctx, botA.Whoami(), roomdb.RoleMember)
theBots[indexSrv].srv.Members.Add(ctx, botA.Whoami(), roomdb.RoleMember)
// allow bots to dial the remote
theBots[indexA].Members.Add(ctx, serv.Whoami(), roomdb.RoleMember)
theBots[indexB].Members.Add(ctx, serv.Whoami(), roomdb.RoleMember)
theBots[indexA].srv.Members.Add(ctx, serv.Whoami(), roomdb.RoleMember)
theBots[indexB].srv.Members.Add(ctx, serv.Whoami(), roomdb.RoleMember)
// dial up B->A and C->A
@ -105,17 +105,17 @@ func TestRoomAnnounce(t *testing.T) {
indexB
)
serv := theBots[indexSrv].Server
botA := theBots[indexA].Server
botB := theBots[indexB].Server
serv := theBots[indexSrv].srv
botA := theBots[indexA].srv
botB := theBots[indexB].srv
// allow both clients
theBots[indexSrv].Members.Add(ctx, botA.Whoami(), roomdb.RoleMember)
theBots[indexSrv].Members.Add(ctx, botB.Whoami(), roomdb.RoleMember)
theBots[indexSrv].srv.Members.Add(ctx, botA.Whoami(), roomdb.RoleMember)
theBots[indexSrv].srv.Members.Add(ctx, botB.Whoami(), roomdb.RoleMember)
// allow bots to dial the remote
theBots[indexA].Members.Add(ctx, serv.Whoami(), roomdb.RoleMember)
theBots[indexB].Members.Add(ctx, serv.Whoami(), roomdb.RoleMember)
theBots[indexA].srv.Members.Add(ctx, serv.Whoami(), roomdb.RoleMember)
theBots[indexB].srv.Members.Add(ctx, serv.Whoami(), roomdb.RoleMember)
// should work (we allowed A)
err := botA.Network.Connect(ctx, serv.Network.GetListenAddr())
@ -169,7 +169,7 @@ func TestRoomAnnounce(t *testing.T) {
var ret bool
err = endpointA.Async(ctx, &ret, muxrpc.TypeJSON, muxrpc.Method{"tunnel", "announce"})
r.NoError(err)
a.False(ret) // <ascii-shrugg>
a.True(ret)
select {
case <-time.After(10 * time.Second):
@ -182,7 +182,7 @@ func TestRoomAnnounce(t *testing.T) {
err = endpointA.Async(ctx, &ret, muxrpc.TypeJSON, muxrpc.Method{"tunnel", "leave"})
r.NoError(err)
a.False(ret) // <ascii-shrugg>
a.True(ret)
select {
case <-time.After(10 * time.Second):

View File

@ -71,7 +71,16 @@ func (bs botServer) Serve(s *roomsrv.Server) func() error {
}
}
func makeNamedTestBot(t testing.TB, name string, opts []roomsrv.Option) (roomdb.MembersService, *roomsrv.Server) {
type testSession struct {
t testing.TB
srv *roomsrv.Server
ctx context.Context
serveGroup *errgroup.Group
}
func makeNamedTestBot(t testing.TB, name string, ctx context.Context, opts []roomsrv.Option) *testSession {
r := require.New(t)
testPath := filepath.Join("testrun", t.Name(), "bot-"+name)
os.RemoveAll(testPath)
@ -108,22 +117,26 @@ func makeNamedTestBot(t testing.TB, name string, opts []roomsrv.Option) (roomdb.
sb := signinwithssb.NewSignalBridge()
theBot, err := roomsrv.New(db.Members, db.DeniedKeys, db.Aliases, db.AuthWithSSB, sb, db.Config, netInfo, botOptions...)
r.NoError(err)
return db.Members, theBot
ts := testSession{
t: t,
srv: theBot,
}
ts.serveGroup, ts.ctx = errgroup.WithContext(ctx)
ts.serveGroup.Go(func() error {
return theBot.Network.Serve(ts.ctx)
})
return &ts
}
type testBot struct {
Server *roomsrv.Server
Members roomdb.MembersService
}
func createServerAndBots(t *testing.T, ctx context.Context, count uint) []testBot {
// TODO: refactor for single test session and use makeTestClient()
func createServerAndBots(t *testing.T, ctx context.Context, count uint) []*testSession {
testInit(t)
r := require.New(t)
botgroup, ctx := errgroup.WithContext(ctx)
bs := newBotServer(ctx, mainLog)
appKey := make([]byte, 32)
rand.Read(appKey)
@ -131,31 +144,25 @@ func createServerAndBots(t *testing.T, ctx context.Context, count uint) []testBo
roomsrv.WithAppKey(appKey),
roomsrv.WithContext(ctx),
}
theBots := []testBot{}
srvsMembers, serv := makeNamedTestBot(t, "srv", netOpts)
botgroup.Go(bs.Serve(serv))
theBots = append(theBots, testBot{
Server: serv,
Members: srvsMembers,
})
theBots := []*testSession{}
session := makeNamedTestBot(t, "srv", ctx, netOpts)
theBots = append(theBots, session)
for i := uint(1); i < count+1; i++ {
botMembers, botSrv := makeNamedTestBot(t, fmt.Sprintf("%d", i), netOpts)
botgroup.Go(bs.Serve(botSrv))
theBots = append(theBots, testBot{
Server: botSrv,
Members: botMembers,
})
// TODO: replace with makeClient?!
clientSession := makeNamedTestBot(t, fmt.Sprintf("%d", i), ctx, netOpts)
theBots = append(theBots, clientSession)
}
t.Cleanup(func() {
time.Sleep(1 * time.Second)
for _, bot := range theBots {
bot.Server.Shutdown()
r.NoError(bot.Server.Close())
bot.srv.Shutdown()
r.NoError(bot.srv.Close())
}
r.NoError(botgroup.Wait())
})
return theBots

View File

@ -36,7 +36,8 @@ func TestWebsocketDialing(t *testing.T) {
t.Cleanup(cancel)
// create the roomsrv
serverMembers, server := makeNamedTestBot(t, "server", nil)
session := makeNamedTestBot(t, "server", ctx, nil)
server := session.srv
// open a TCP listener for HTTP
l, err := net.Listen("tcp4", "localhost:0")
@ -53,7 +54,7 @@ func TestWebsocketDialing(t *testing.T) {
r.NoError(err)
// add it as a memeber
memberID, err := serverMembers.Add(ctx, client.Feed, roomdb.RoleMember)
memberID, err := server.Members.Add(ctx, client.Feed, roomdb.RoleMember)
r.NoError(err)
t.Log("client member:", memberID)

View File

@ -125,7 +125,6 @@ func (m *Manager) AlreadyAdded(who refs.FeedRef, edp muxrpc.Endpoint) bool {
m.updater.Update(m.room.AsList())
}
// send the current state
m.roomMu.Unlock()
return has
}