1// Copyright (C) 2019 Storj Labs, Inc. 2// See LICENSE for copying information. 3 4package trust_test 5 6import ( 7 "context" 8 "crypto/x509" 9 "errors" 10 "fmt" 11 "sync" 12 "testing" 13 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/require" 16 "go.uber.org/zap/zaptest" 17 18 "storj.io/common/identity" 19 "storj.io/common/storj" 20 "storj.io/common/testcontext" 21 "storj.io/common/testrand" 22 "storj.io/storj/storagenode/trust" 23) 24 25func TestPoolRequiresCachePath(t *testing.T) { 26 log := zaptest.NewLogger(t) 27 _, err := trust.NewPool(log, newFakeIdentityResolver(), trust.Config{}, nil) 28 require.EqualError(t, err, "trust: cache path cannot be empty") 29} 30 31func TestPoolVerifySatelliteID(t *testing.T) { 32 ctx, pool, source, _ := newPoolTest(t) 33 defer ctx.Cleanup() 34 35 id := testrand.NodeID() 36 37 // Assert the ID is not trusted 38 err := pool.VerifySatelliteID(context.Background(), id) 39 require.EqualError(t, err, fmt.Sprintf("trust: satellite %q is untrusted", id)) 40 41 // Refresh the pool with the new trust entry 42 source.entries = []trust.Entry{ 43 { 44 SatelliteURL: trust.SatelliteURL{ 45 ID: id, 46 Host: "foo.test", 47 Port: 7777, 48 }, 49 }, 50 } 51 require.NoError(t, pool.Refresh(context.Background())) 52 53 // Assert the ID is now trusted 54 err = pool.VerifySatelliteID(context.Background(), id) 55 require.NoError(t, err) 56 57 // Refresh the pool after removing the trusted satellite 58 source.entries = nil 59 require.NoError(t, pool.Refresh(context.Background())) 60 61 // Assert the ID is no longer trusted 62 err = pool.VerifySatelliteID(context.Background(), id) 63 require.EqualError(t, err, fmt.Sprintf("trust: satellite %q is untrusted", id)) 64} 65 66func TestPoolGetSignee(t *testing.T) { 67 id := testrand.NodeID() 68 url := trust.SatelliteURL{ 69 ID: id, 70 Host: "foo.test", 71 Port: 7777, 72 } 73 74 ctx, pool, source, resolver := newPoolTest(t) 75 defer ctx.Cleanup() 76 77 // ID is untrusted 78 _, err := pool.GetSignee(context.Background(), id) 79 require.EqualError(t, err, fmt.Sprintf("trust: satellite %q is untrusted", id)) 80 81 // Refresh the pool with the new trust entry 82 source.entries = []trust.Entry{{SatelliteURL: url}} 83 require.NoError(t, pool.Refresh(context.Background())) 84 85 // Identity is uncached and resolving fails 86 _, err = pool.GetSignee(context.Background(), id) 87 require.EqualError(t, err, "trust: no identity") 88 89 // Now make resolving succeed 90 identity := &identity.PeerIdentity{ 91 ID: id, 92 Leaf: &x509.Certificate{}, 93 } 94 resolver.SetIdentity(url.NodeURL(), identity) 95 signee, err := pool.GetSignee(context.Background(), id) 96 require.NoError(t, err) 97 assert.Equal(t, id, signee.ID()) 98 99 // Now make resolving fail but ensure we can still get the signee since 100 // the identity is cached. 101 resolver.SetIdentity(url.NodeURL(), nil) 102 signee, err = pool.GetSignee(context.Background(), id) 103 require.NoError(t, err) 104 assert.Equal(t, id, signee.ID()) 105 106 // Now update the address on the entry and assert that the identity is 107 // reset in the cache and needs to be refetched (and fails since we've 108 // hampered the resolver) 109 url.Host = "bar.test" 110 source.entries = []trust.Entry{{SatelliteURL: url}} 111 require.NoError(t, pool.Refresh(context.Background())) 112 _, err = pool.GetSignee(context.Background(), id) 113 require.EqualError(t, err, "trust: no identity") 114} 115 116func TestPoolGetSatellites(t *testing.T) { 117 ctx, pool, source, _ := newPoolTest(t) 118 defer ctx.Cleanup() 119 120 id1 := testrand.NodeID() 121 id2 := testrand.NodeID() 122 123 // Refresh the pool with the new trust entry 124 source.entries = []trust.Entry{ 125 { 126 SatelliteURL: trust.SatelliteURL{ 127 ID: id1, 128 Host: "foo.test", 129 Port: 7777, 130 }, 131 }, 132 { 133 SatelliteURL: trust.SatelliteURL{ 134 ID: id2, 135 Host: "bar.test", 136 Port: 7777, 137 }, 138 }, 139 } 140 require.NoError(t, pool.Refresh(context.Background())) 141 142 expected := []storj.NodeID{id1, id2} 143 actual := pool.GetSatellites(context.Background()) 144 assert.ElementsMatch(t, expected, actual) 145} 146 147func TestPoolGetAddress(t *testing.T) { 148 ctx, pool, source, _ := newPoolTest(t) 149 defer ctx.Cleanup() 150 151 id := testrand.NodeID() 152 153 // Assert the ID is not trusted 154 nodeurl, err := pool.GetNodeURL(context.Background(), id) 155 require.EqualError(t, err, fmt.Sprintf("trust: satellite %q is untrusted", id)) 156 require.Empty(t, nodeurl) 157 158 // Refresh the pool with the new trust entry 159 source.entries = []trust.Entry{ 160 { 161 SatelliteURL: trust.SatelliteURL{ 162 ID: id, 163 Host: "foo.test", 164 Port: 7777, 165 }, 166 }, 167 } 168 require.NoError(t, pool.Refresh(context.Background())) 169 170 // Assert the ID is now trusted and the correct address is returned 171 nodeurl, err = pool.GetNodeURL(context.Background(), id) 172 require.NoError(t, err) 173 require.Equal(t, id, nodeurl.ID) 174 require.Equal(t, "foo.test:7777", nodeurl.Address) 175 176 // Refresh the pool with an updated trust entry with a new address 177 source.entries = []trust.Entry{ 178 { 179 SatelliteURL: trust.SatelliteURL{ 180 ID: id, 181 Host: "bar.test", 182 Port: 7777, 183 }, 184 }, 185 } 186 require.NoError(t, pool.Refresh(context.Background())) 187 188 // Assert the ID is now trusted and the correct address is returned 189 nodeurl, err = pool.GetNodeURL(context.Background(), id) 190 require.NoError(t, err) 191 require.Equal(t, id, nodeurl.ID) 192 require.Equal(t, "bar.test:7777", nodeurl.Address) 193} 194 195func newPoolTest(t *testing.T) (*testcontext.Context, *trust.Pool, *fakeSource, *fakeIdentityResolver) { 196 ctx := testcontext.New(t) 197 198 source := &fakeSource{} 199 200 resolver := newFakeIdentityResolver() 201 202 log := zaptest.NewLogger(t) 203 pool, err := trust.NewPool(log, resolver, trust.Config{ 204 Sources: []trust.Source{source}, 205 CachePath: ctx.File("trust-cache.json"), 206 }, nil) 207 if err != nil { 208 ctx.Cleanup() 209 require.NoError(t, err) 210 } 211 212 return ctx, pool, source, resolver 213} 214 215type fakeIdentityResolver struct { 216 mu sync.Mutex 217 identities map[storj.NodeURL]*identity.PeerIdentity 218} 219 220func newFakeIdentityResolver() *fakeIdentityResolver { 221 return &fakeIdentityResolver{ 222 identities: make(map[storj.NodeURL]*identity.PeerIdentity), 223 } 224} 225 226func (resolver *fakeIdentityResolver) SetIdentity(url storj.NodeURL, identity *identity.PeerIdentity) { 227 resolver.mu.Lock() 228 defer resolver.mu.Unlock() 229 resolver.identities[url] = identity 230} 231 232func (resolver *fakeIdentityResolver) ResolveIdentity(ctx context.Context, url storj.NodeURL) (*identity.PeerIdentity, error) { 233 resolver.mu.Lock() 234 defer resolver.mu.Unlock() 235 236 identity := resolver.identities[url] 237 if identity == nil { 238 return nil, errors.New("no identity") 239 } 240 return identity, nil 241} 242