Skip to content
File

Blob: tun/server/delegation_rpc_test.go

go100 lines
1package server
2 
3import (
4 "errors"
5 "strings"
6 "testing"
7 "time"
8 
9 "go.miragespace.co/specter/spec/mocks"
10 "go.miragespace.co/specter/spec/protocol"
11 "go.miragespace.co/specter/spec/rpc"
12 "go.miragespace.co/specter/spec/transport"
13 "go.miragespace.co/specter/spec/tun"
14 
15 "github.com/stretchr/testify/mock"
16 "github.com/stretchr/testify/require"
17 "github.com/twitchtv/twirp"
18)
19 
20func TestDelegationLifecycle(t *testing.T) {
21 t.Run("owner authentication", func(t *testing.T) {
22 s, n, _, cert := sessionFixture(t)
23 tp := mocks.SelfTransport()
24 tp.WithCertificate(cert)
25 router := transport.NewStreamRouter(s.Logger, nil, tp)
26 s.AttachRouter(t.Context(), router)
27 go router.Accept(t.Context())
28 cli := rpc.DynamicTunnelClient(rpc.DisablePooling(t.Context()), tp)
29 n.On("Get", mock.Anything, mock.Anything).Return(nil, nil)
30 ctx := rpc.WithNode(t.Context(), &protocol.Node{Address: "client"})
31 _, err := cli.MintDelegation(ctx, &protocol.MintDelegationRequest{Hostname: "test"})
32 require.Equal(t, twirp.Unauthenticated, err.(twirp.Error).Code())
33 _, err = cli.ListDelegations(ctx, &protocol.ListDelegationsRequest{})
34 require.Equal(t, twirp.Unauthenticated, err.(twirp.Error).Code())
35 _, err = cli.RevokeDelegation(ctx, &protocol.RevokeDelegationRequest{})
36 require.Equal(t, twirp.Unauthenticated, err.(twirp.Error).Code())
37 _, err = cli.PublishTunnel(ctx, &protocol.PublishTunnelRequest{
38 Hostname: "test",
39 Servers: []*protocol.Node{{Address: "server"}},
40 })
41 require.Equal(t, twirp.Unauthenticated, err.(twirp.Error).Code())
42 n.AssertNotCalled(t, "Acquire", mock.Anything, mock.Anything, mock.Anything)
43 n.AssertNotCalled(t, "Put", mock.Anything, mock.Anything, mock.Anything)
44 })
45 t.Run("mint list revoke", func(t *testing.T) {
46 s, n, _, cert := sessionFixture(t)
47 ctx, _ := sessionContext(t, cert)
48 owner, _, err := extractAuthenticated(ctx)
49 require.NoError(t, err)
50 index := []byte(tun.ClientDelegationsPrefix(owner))
51 n.On("PrefixContains", mock.Anything, []byte(tun.ClientHostnamesPrefix(owner)), []byte("test")).Return(true, nil)
52 n.On("PrefixList", mock.Anything, index).Return([][]byte{}, nil).Once()
53 var rec protocol.DelegationRecord
54 appended := n.On("PrefixAppend", mock.Anything, index, mock.Anything).Return(nil).Once()
55 n.On("Put", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
56 require.NoError(t, rec.UnmarshalVT(args.Get(2).([]byte)))
57 require.Equal(t, []byte(tun.DelegationKey(rec.Id)), args.Get(1))
58 }).Return(nil).Once().NotBefore(appended)
59 minted, err := s.MintDelegation(ctx, &protocol.MintDelegationRequest{Hostname: "test"})
60 require.NoError(t, err)
61 secret, err := tun.ParseDelegationToken(minted.Token)
62 require.NoError(t, err)
63 require.Equal(t, tun.DelegationID(secret), minted.Grant.Id)
64 require.Zero(t, minted.Grant.ExpiresAt)
65 missing := strings.Repeat("0", 64)
66 n.On("PrefixList", mock.Anything, index).Return([][]byte{[]byte(rec.Id), []byte(missing)}, nil).Once()
67 data, _ := rec.MarshalVT()
68 n.On("Get", mock.Anything, []byte(tun.DelegationKey(rec.Id))).Return(data, nil).Twice()
69 n.On("Get", mock.Anything, []byte(tun.DelegationKey(missing))).Return(nil, nil).Once()
70 listed, err := s.ListDelegations(ctx, &protocol.ListDelegationsRequest{})
71 require.NoError(t, err)
72 require.Len(t, listed.Grants, 2)
73 require.True(t, listed.Grants[1].Incomplete)
74 require.NotContains(t, listed.String(), "tg1_")
75 deleted := n.On("Delete", mock.Anything, []byte(tun.DelegationKey(rec.Id))).Return(nil).Once()
76 n.On("PrefixRemove", mock.Anything, index, []byte(rec.Id)).Return(errors.New("index unavailable")).Once().NotBefore(deleted)
77 revoked, err := s.RevokeDelegation(ctx, &protocol.RevokeDelegationRequest{Id: rec.Id})
78 require.NoError(t, err)
79 require.True(t, revoked.Revoked)
80 require.NotEmpty(t, revoked.IndexError)
81 n.On("Delete", mock.Anything, []byte(tun.DelegationKey(rec.Id))).Return(nil).Once()
82 n.On("Get", mock.Anything, []byte(tun.DelegationKey(rec.Id))).Return(nil, nil).Once()
83 n.On("PrefixContains", mock.Anything, index, []byte(rec.Id)).Return(true, nil).Once()
84 n.On("PrefixRemove", mock.Anything, index, []byte(rec.Id)).Return(nil).Once()
85 revoked, err = s.RevokeDelegation(ctx, &protocol.RevokeDelegationRequest{Id: rec.Id})
86 require.NoError(t, err)
87 require.True(t, revoked.Revoked)
88 require.Empty(t, revoked.IndexError)
89 _, err = s.MintDelegation(ctx, &protocol.MintDelegationRequest{
90 Hostname: "test",
91 ExpiresAt: time.Now().Add(-time.Hour).Unix(),
92 })
93 require.Equal(t, twirp.InvalidArgument, err.(twirp.Error).Code())
94 n.On("PrefixList", mock.Anything, index).Return(make([][]byte, 128), nil).Once()
95 _, err = s.MintDelegation(ctx, &protocol.MintDelegationRequest{Hostname: "test"})
96 require.Equal(t, twirp.ResourceExhausted, err.(twirp.Error).Code())
97 n.AssertExpectations(t)
98 })
99}