github.com/decred/dcrlnd@v0.7.6/lntest/itest/lnd_rpc_middleware_interceptor_test.go (about)

     1  package itest
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/decred/dcrd/dcrutil/v4"
    10  	"github.com/decred/dcrlnd/lnrpc"
    11  	"github.com/decred/dcrlnd/lntest"
    12  	"github.com/decred/dcrlnd/macaroons"
    13  	"github.com/stretchr/testify/require"
    14  	"google.golang.org/protobuf/proto"
    15  	"gopkg.in/macaroon.v2"
    16  )
    17  
    18  // testRPCMiddlewareInterceptor tests that the RPC middleware interceptor can
    19  // be used correctly and in a safe way.
    20  func testRPCMiddlewareInterceptor(net *lntest.NetworkHarness, t *harnessTest) {
    21  	// Let's first enable the middleware interceptor.
    22  	aliceOriginalArgs := net.Alice.Cfg.ExtraArgs
    23  	net.Alice.Cfg.ExtraArgs = append(
    24  		net.Alice.Cfg.ExtraArgs, "--rpcmiddleware.enable",
    25  	)
    26  	err := net.RestartNode(net.Alice, nil)
    27  	require.NoError(t.t, err)
    28  
    29  	// Let's set up a channel between Alice and Bob, just to get some useful
    30  	// data to inspect when doing RPC calls to Alice later.
    31  	net.EnsureConnected(t.t, net.Alice, net.Bob)
    32  	net.SendCoins(t.t, dcrutil.AtomsPerCoin, net.Alice)
    33  	chanAlice := openChannelAndAssert(
    34  		t, net, net.Alice, net.Bob, lntest.OpenChannelParams{
    35  			Amt: 1_234_567,
    36  		},
    37  	)
    38  	defer closeChannelAndAssert(t, net, net.Alice, chanAlice, false)
    39  
    40  	// Load or bake the macaroons that the simulated users will use to
    41  	// access the RPC.
    42  	readonlyMac, err := net.Alice.ReadMacaroon(
    43  		net.Alice.ReadMacPath(), defaultTimeout,
    44  	)
    45  	require.NoError(t.t, err)
    46  
    47  	customCaveatMac, err := macaroons.SafeCopyMacaroon(readonlyMac)
    48  	require.NoError(t.t, err)
    49  	addConstraint := macaroons.CustomConstraint(
    50  		"itest-caveat", "itest-value",
    51  	)
    52  	require.NoError(t.t, addConstraint(customCaveatMac))
    53  
    54  	// Run all sub-tests now. We can't run anything in parallel because that
    55  	// would cause the main test function to exit and the nodes being
    56  	// cleaned up.
    57  	t.t.Run("registration restrictions", func(tt *testing.T) {
    58  		middlewareRegistrationRestrictionTests(tt, net.Alice)
    59  	})
    60  	t.t.Run("read-only intercept", func(tt *testing.T) {
    61  		registration := registerMiddleware(
    62  			tt, net.Alice, &lnrpc.MiddlewareRegistration{
    63  				MiddlewareName: "itest-interceptor",
    64  				ReadOnlyMode:   true,
    65  			},
    66  		)
    67  		defer registration.cancel()
    68  
    69  		middlewareInterceptionTest(
    70  			tt, net.Alice, net.Bob, registration, readonlyMac,
    71  			customCaveatMac, true,
    72  		)
    73  	})
    74  
    75  	// We've manually disconnected Bob from Alice in the previous test, make
    76  	// sure they're connected again.
    77  	net.EnsureConnected(t.t, net.Alice, net.Bob)
    78  	t.t.Run("encumbered macaroon intercept", func(tt *testing.T) {
    79  		registration := registerMiddleware(
    80  			tt, net.Alice, &lnrpc.MiddlewareRegistration{
    81  				MiddlewareName:           "itest-interceptor",
    82  				CustomMacaroonCaveatName: "itest-caveat",
    83  			},
    84  		)
    85  		defer registration.cancel()
    86  
    87  		middlewareInterceptionTest(
    88  			tt, net.Alice, net.Bob, registration, customCaveatMac,
    89  			readonlyMac, false,
    90  		)
    91  	})
    92  
    93  	// Next, run the response manipulation tests.
    94  	net.EnsureConnected(t.t, net.Alice, net.Bob)
    95  	t.t.Run("read-only not allowed to manipulate", func(tt *testing.T) {
    96  		registration := registerMiddleware(
    97  			tt, net.Alice, &lnrpc.MiddlewareRegistration{
    98  				MiddlewareName: "itest-interceptor",
    99  				ReadOnlyMode:   true,
   100  			},
   101  		)
   102  		defer registration.cancel()
   103  
   104  		middlewareManipulationTest(
   105  			tt, net.Alice, net.Bob, registration, readonlyMac, true,
   106  		)
   107  	})
   108  	net.EnsureConnected(t.t, net.Alice, net.Bob)
   109  	t.t.Run("encumbered macaroon manipulate", func(tt *testing.T) {
   110  		registration := registerMiddleware(
   111  			tt, net.Alice, &lnrpc.MiddlewareRegistration{
   112  				MiddlewareName:           "itest-interceptor",
   113  				CustomMacaroonCaveatName: "itest-caveat",
   114  			},
   115  		)
   116  		defer registration.cancel()
   117  
   118  		middlewareManipulationTest(
   119  			tt, net.Alice, net.Bob, registration, customCaveatMac,
   120  			false,
   121  		)
   122  	})
   123  
   124  	// And finally make sure mandatory middleware is always checked for any
   125  	// RPC request.
   126  	t.t.Run("mandatory middleware", func(tt *testing.T) {
   127  		if net.Alice.Cfg.RemoteWallet {
   128  			tt.Skipf("Skipping test because we cannot init a new " +
   129  				"remote wallet when the required middleware is " +
   130  				"offline")
   131  		}
   132  		alice := net.NewNode(t.t, "alice", []string{"--rpcmiddleware.enable"})
   133  		middlewareMandatoryTest(tt, alice, net)
   134  	})
   135  
   136  	net.Alice.Cfg.ExtraArgs = aliceOriginalArgs
   137  	err = net.RestartNode(net.Alice, nil)
   138  	require.NoError(t.t, err)
   139  }
   140  
   141  // middlewareRegistrationRestrictionTests tests all restrictions that apply to
   142  // registering a middleware.
   143  func middlewareRegistrationRestrictionTests(t *testing.T,
   144  	node *lntest.HarnessNode) {
   145  
   146  	testCases := []struct {
   147  		registration *lnrpc.MiddlewareRegistration
   148  		expectedErr  string
   149  	}{{
   150  		registration: &lnrpc.MiddlewareRegistration{
   151  			MiddlewareName: "foo",
   152  		},
   153  		expectedErr: "invalid middleware name",
   154  	}, {
   155  		registration: &lnrpc.MiddlewareRegistration{
   156  			MiddlewareName:           "itest-interceptor",
   157  			CustomMacaroonCaveatName: "foo",
   158  		},
   159  		expectedErr: "custom caveat name of at least",
   160  	}, {
   161  		registration: &lnrpc.MiddlewareRegistration{
   162  			MiddlewareName:           "itest-interceptor",
   163  			CustomMacaroonCaveatName: "itest-caveat",
   164  			ReadOnlyMode:             true,
   165  		},
   166  		expectedErr: "cannot set read-only and custom caveat name",
   167  	}}
   168  
   169  	for idx, tc := range testCases {
   170  		tc := tc
   171  
   172  		t.Run(fmt.Sprintf("%d", idx), func(tt *testing.T) {
   173  			invalidName := registerMiddleware(
   174  				tt, node, tc.registration,
   175  			)
   176  			_, err := invalidName.stream.Recv()
   177  			require.Error(tt, err)
   178  			require.Contains(tt, err.Error(), tc.expectedErr)
   179  
   180  			invalidName.cancel()
   181  		})
   182  	}
   183  }
   184  
   185  // middlewareInterceptionTest tests that unary and streaming requests can be
   186  // intercepted. It also makes sure that depending on the mode (read-only or
   187  // custom macaroon caveat) a middleware only gets access to the requests it
   188  // should be allowed access to.
   189  func middlewareInterceptionTest(t *testing.T, node *lntest.HarnessNode,
   190  	peer *lntest.HarnessNode, registration *middlewareHarness,
   191  	userMac *macaroon.Macaroon, disallowedMac *macaroon.Macaroon,
   192  	readOnly bool) {
   193  
   194  	// Everything we test here should be executed in a matter of
   195  	// milliseconds, so we can use one single timeout context for all calls.
   196  	ctxb := context.Background()
   197  	ctxc, cancel := context.WithTimeout(ctxb, defaultTimeout)
   198  	defer cancel()
   199  
   200  	// Create a client connection that we'll use to simulate user requests
   201  	// to lnd with.
   202  	cleanup, client := macaroonClient(t, node, userMac)
   203  	defer cleanup()
   204  
   205  	// We're going to send a simple RPC request to list all channels.
   206  	// We need to invoke the intercept logic in a goroutine because we'd
   207  	// block the execution of the main task otherwise.
   208  	req := &lnrpc.ListChannelsRequest{ActiveOnly: true}
   209  	go registration.interceptUnary(
   210  		"/lnrpc.Lightning/ListChannels", req, nil, readOnly,
   211  	)
   212  
   213  	// Do the actual call now and wait for the interceptor to do its thing.
   214  	resp, err := client.ListChannels(ctxc, req)
   215  	require.NoError(t, err)
   216  
   217  	// Did we receive the correct intercept message?
   218  	assertInterceptedType(t, resp, <-registration.responsesChan)
   219  
   220  	// Let's test the same for a streaming endpoint.
   221  	req2 := &lnrpc.PeerEventSubscription{}
   222  	go registration.interceptStream(
   223  		"/lnrpc.Lightning/SubscribePeerEvents", req2, nil, readOnly,
   224  	)
   225  
   226  	// Do the actual call now and wait for the interceptor to do its thing.
   227  	peerCtx, peerCancel := context.WithCancel(ctxb)
   228  	resp2, err := client.SubscribePeerEvents(peerCtx, req2)
   229  	require.NoError(t, err)
   230  
   231  	// Disconnect Bob to trigger a peer event without using Alice's RPC
   232  	// interface itself.
   233  	_, err = peer.DisconnectPeer(ctxc, &lnrpc.DisconnectPeerRequest{
   234  		PubKey: node.PubKeyStr,
   235  	})
   236  	require.NoError(t, err)
   237  	peerEvent, err := resp2.Recv()
   238  	require.NoError(t, err)
   239  	require.Equal(t, lnrpc.PeerEvent_PEER_OFFLINE, peerEvent.GetType())
   240  
   241  	// Stop the peer stream again, otherwise we'll produce more events.
   242  	peerCancel()
   243  
   244  	// Did we receive the correct intercept message?
   245  	assertInterceptedType(t, peerEvent, <-registration.responsesChan)
   246  
   247  	// Make sure that with the other macaroon we aren't allowed to access
   248  	// the interceptor. If we registered for read-only access then there is
   249  	// no middleware that handles the custom macaroon caveat. If we
   250  	// registered for a custom caveat then there is no middleware that
   251  	// handles unencumbered read-only access.
   252  	cleanup, client = macaroonClient(t, node, disallowedMac)
   253  	defer cleanup()
   254  
   255  	// We need to make sure we don't get any interception messages for
   256  	// requests made with the disallowed macaroon.
   257  	var (
   258  		errChan = make(chan error, 1)
   259  		msgChan = make(chan *lnrpc.RPCMiddlewareRequest, 1)
   260  	)
   261  	go func() {
   262  		req, err := registration.stream.Recv()
   263  		if err != nil {
   264  			errChan <- err
   265  			return
   266  		}
   267  
   268  		msgChan <- req
   269  	}()
   270  
   271  	// Let's invoke the same request again but with the other macaroon.
   272  	resp, err = client.ListChannels(ctxc, req)
   273  
   274  	// Depending on what mode we're in, we expect something different. If we
   275  	// are in read-only mode then an encumbered macaroon cannot be used
   276  	// since there is no middleware registered for it. If we registered for
   277  	// a custom macaroon caveat and a request with anon-encumbered macaroon
   278  	// comes in, we expect to just not get any intercept messages.
   279  	if readOnly {
   280  		require.Error(t, err)
   281  		require.Contains(
   282  			t, err.Error(), "cannot accept macaroon with custom "+
   283  				"caveat 'itest-caveat', no middleware "+
   284  				"registered",
   285  		)
   286  	} else {
   287  		require.NoError(t, err)
   288  
   289  		// We disconnected Bob so there should be no active channels.
   290  		require.Len(t, resp.Channels, 0)
   291  	}
   292  
   293  	// There should be neither an error nor any interception messages in the
   294  	// channels.
   295  	select {
   296  	case err := <-errChan:
   297  		t.Fatalf("Unexpected error, not expecting messages: %v", err)
   298  
   299  	case msg := <-msgChan:
   300  		t.Fatalf("Unexpected intercept message: %v", msg)
   301  
   302  	case <-time.After(time.Second):
   303  		// Nothing came in for a second, we're fine.
   304  	}
   305  }
   306  
   307  // middlewareManipulationTest tests that unary and streaming requests can be
   308  // intercepted and also manipulated, at least if the middleware didn't register
   309  // for read-only access.
   310  func middlewareManipulationTest(t *testing.T, node *lntest.HarnessNode,
   311  	peer *lntest.HarnessNode, registration *middlewareHarness,
   312  	userMac *macaroon.Macaroon, readOnly bool) {
   313  
   314  	// Everything we test here should be executed in a matter of
   315  	// milliseconds, so we can use one single timeout context for all calls.
   316  	ctxb := context.Background()
   317  	ctxc, cancel := context.WithTimeout(ctxb, defaultTimeout)
   318  	defer cancel()
   319  
   320  	// Create a client connection that we'll use to simulate user requests
   321  	// to lnd with.
   322  	cleanup, client := macaroonClient(t, node, userMac)
   323  	defer cleanup()
   324  
   325  	// We're going to attempt to replace the response with our own. But
   326  	// since we only registered for read-only access, our replacement should
   327  	// just be ignored.
   328  	replacementResponse := &lnrpc.ListChannelsResponse{
   329  		Channels: []*lnrpc.Channel{{
   330  			ChannelPoint: "f000:0",
   331  		}, {
   332  			ChannelPoint: "f000:1",
   333  		}},
   334  	}
   335  
   336  	// We're going to send a simple RPC request to list all channels.
   337  	// We need to invoke the intercept logic in a goroutine because we'd
   338  	// block the execution of the main task otherwise.
   339  	req := &lnrpc.ListChannelsRequest{ActiveOnly: true}
   340  	go registration.interceptUnary(
   341  		"/lnrpc.Lightning/ListChannels", req, replacementResponse,
   342  		readOnly,
   343  	)
   344  
   345  	// Do the actual call now and wait for the interceptor to do its thing.
   346  	resp, err := client.ListChannels(ctxc, req)
   347  	require.NoError(t, err)
   348  
   349  	// Did we get the manipulated response (2 fake channels) or the original
   350  	// one (1 channel)?
   351  	if readOnly {
   352  		require.Len(t, resp.Channels, 1)
   353  	} else {
   354  		require.Len(t, resp.Channels, 2)
   355  	}
   356  
   357  	// Let's test the same for a streaming endpoint.
   358  	replacementResponse2 := &lnrpc.PeerEvent{
   359  		Type:   lnrpc.PeerEvent_PEER_ONLINE,
   360  		PubKey: "foo",
   361  	}
   362  	req2 := &lnrpc.PeerEventSubscription{}
   363  	go registration.interceptStream(
   364  		"/lnrpc.Lightning/SubscribePeerEvents", req2,
   365  		replacementResponse2, readOnly,
   366  	)
   367  
   368  	// Do the actual call now and wait for the interceptor to do its thing.
   369  	peerCtx, peerCancel := context.WithCancel(ctxb)
   370  	resp2, err := client.SubscribePeerEvents(peerCtx, req2)
   371  	require.NoError(t, err)
   372  
   373  	// Disconnect Bob to trigger a peer event without using Alice's RPC
   374  	// interface itself.
   375  	_, err = peer.DisconnectPeer(ctxc, &lnrpc.DisconnectPeerRequest{
   376  		PubKey: node.PubKeyStr,
   377  	})
   378  	require.NoError(t, err)
   379  	peerEvent, err := resp2.Recv()
   380  	require.NoError(t, err)
   381  
   382  	// Did we get the correct, original response?
   383  	if readOnly {
   384  		require.Equal(
   385  			t, lnrpc.PeerEvent_PEER_OFFLINE, peerEvent.GetType(),
   386  		)
   387  		require.Equal(t, peer.PubKeyStr, peerEvent.PubKey)
   388  	} else {
   389  		require.Equal(
   390  			t, lnrpc.PeerEvent_PEER_ONLINE, peerEvent.GetType(),
   391  		)
   392  		require.Equal(t, "foo", peerEvent.PubKey)
   393  	}
   394  
   395  	// Stop the peer stream again, otherwise we'll produce more events.
   396  	peerCancel()
   397  }
   398  
   399  // middlewareMandatoryTest tests that all RPC requests are blocked if there is
   400  // a mandatory middleware declared that's currently not registered.
   401  func middlewareMandatoryTest(t *testing.T, node *lntest.HarnessNode,
   402  	net *lntest.NetworkHarness) {
   403  
   404  	// Let's declare our itest interceptor as mandatory but don't register
   405  	// it just yet. That should cause all RPC requests to fail, except for
   406  	// the registration itself.
   407  	node.Cfg.ExtraArgs = append(
   408  		node.Cfg.ExtraArgs,
   409  		"--rpcmiddleware.addmandatory=itest-interceptor",
   410  	)
   411  	err := net.RestartNodeNoUnlock(node, nil, false)
   412  	require.NoError(t, err)
   413  
   414  	// The "wait for node to start" flag of the above restart does too much
   415  	// and has a call to GetInfo built in, which will fail in this special
   416  	// test case. So we need to do the wait and client setup manually here.
   417  	conn, err := node.ConnectRPC(true)
   418  	require.NoError(t, err)
   419  	node.InitRPCClients(conn)
   420  	err = node.WaitUntilStateReached(lnrpc.WalletState_RPC_ACTIVE)
   421  	require.NoError(t, err)
   422  	node.LightningClient = lnrpc.NewLightningClient(conn)
   423  
   424  	ctxb := context.Background()
   425  	ctxc, cancel := context.WithTimeout(ctxb, defaultTimeout)
   426  	defer cancel()
   427  
   428  	// Test a unary request first.
   429  	_, err = node.ListChannels(ctxc, &lnrpc.ListChannelsRequest{})
   430  	require.Error(t, err)
   431  	require.Contains(
   432  		t, err.Error(), "middleware 'itest-interceptor' is "+
   433  			"currently not registered",
   434  	)
   435  
   436  	// Then a streaming one.
   437  	stream, err := node.SubscribeInvoices(ctxc, &lnrpc.InvoiceSubscription{})
   438  	require.NoError(t, err)
   439  	_, err = stream.Recv()
   440  	require.Error(t, err)
   441  	require.Contains(
   442  		t, err.Error(), "middleware 'itest-interceptor' is "+
   443  			"currently not registered",
   444  	)
   445  
   446  	// Now let's register the middleware and try again.
   447  	registration := registerMiddleware(
   448  		t, node, &lnrpc.MiddlewareRegistration{
   449  			MiddlewareName:           "itest-interceptor",
   450  			CustomMacaroonCaveatName: "itest-caveat",
   451  		},
   452  	)
   453  	defer registration.cancel()
   454  
   455  	// Both the unary and streaming requests should now be allowed.
   456  	time.Sleep(500 * time.Millisecond)
   457  	_, err = node.ListChannels(ctxc, &lnrpc.ListChannelsRequest{})
   458  	require.NoError(t, err)
   459  	_, err = node.SubscribeInvoices(ctxc, &lnrpc.InvoiceSubscription{})
   460  	require.NoError(t, err)
   461  
   462  	// We now shut down the node manually to prevent the test from failing
   463  	// because we can't call the stop RPC if we unregister the middleware in
   464  	// the defer statement above.
   465  	err = net.ShutdownNode(node)
   466  	require.NoError(t, err)
   467  }
   468  
   469  // assertInterceptedType makes sure that the intercept message sent by the RPC
   470  // interceptor is correct for a proto message that was sent or received over the
   471  // RPC interface.
   472  func assertInterceptedType(t *testing.T, rpcMessage proto.Message,
   473  	interceptMessage *lnrpc.RPCMessage) {
   474  
   475  	t.Helper()
   476  
   477  	require.Equal(
   478  		t, string(proto.MessageName(rpcMessage)),
   479  		interceptMessage.TypeName,
   480  	)
   481  	rawRequest, err := proto.Marshal(rpcMessage)
   482  	require.NoError(t, err)
   483  
   484  	// Make sure we don't trip over nil vs. empty slice in the equality
   485  	// check below.
   486  	if len(rawRequest) == 0 {
   487  		rawRequest = nil
   488  	}
   489  
   490  	require.Equal(t, rawRequest, interceptMessage.Serialized)
   491  }
   492  
   493  // middlewareStream is a type alias to shorten the long definition.
   494  type middlewareStream lnrpc.Lightning_RegisterRPCMiddlewareClient
   495  
   496  // middlewareHarness is a test harness that holds one instance of a simulated
   497  // middleware.
   498  type middlewareHarness struct {
   499  	t      *testing.T
   500  	cancel func()
   501  	stream middlewareStream
   502  
   503  	responsesChan chan *lnrpc.RPCMessage
   504  }
   505  
   506  // registerMiddleware creates a new middleware harness and sends the initial
   507  // register message to the RPC server.
   508  func registerMiddleware(t *testing.T, node *lntest.HarnessNode,
   509  	registration *lnrpc.MiddlewareRegistration) *middlewareHarness {
   510  
   511  	ctxc, cancel := context.WithCancel(context.Background())
   512  
   513  	middlewareStream, err := node.RegisterRPCMiddleware(ctxc)
   514  	require.NoError(t, err)
   515  
   516  	err = middlewareStream.Send(&lnrpc.RPCMiddlewareResponse{
   517  		MiddlewareMessage: &lnrpc.RPCMiddlewareResponse_Register{
   518  			Register: registration,
   519  		},
   520  	})
   521  	require.NoError(t, err)
   522  
   523  	return &middlewareHarness{
   524  		t:             t,
   525  		cancel:        cancel,
   526  		stream:        middlewareStream,
   527  		responsesChan: make(chan *lnrpc.RPCMessage),
   528  	}
   529  }
   530  
   531  // interceptUnary intercepts a unary call, optionally requesting to replace the
   532  // response sent to the client. A unary call is expected to receive one
   533  // intercept message for the request and one for the response.
   534  //
   535  // NOTE: Must be called in a goroutine as this will block until the response is
   536  // read from the response channel.
   537  func (h *middlewareHarness) interceptUnary(methodURI string,
   538  	expectedRequest proto.Message, responseReplacement proto.Message,
   539  	readOnly bool) {
   540  
   541  	// Read intercept message and make sure it's for an RPC request.
   542  	reqIntercept, err := h.stream.Recv()
   543  	require.NoError(h.t, err)
   544  
   545  	// Make sure the custom condition is populated correctly (if we're using
   546  	// a macaroon with a custom condition).
   547  	if !readOnly {
   548  		require.Equal(
   549  			h.t, "itest-value", reqIntercept.CustomCaveatCondition,
   550  		)
   551  	}
   552  
   553  	req := reqIntercept.GetRequest()
   554  	require.NotNil(h.t, req)
   555  
   556  	// We know the request we're going to send so make sure we get the right
   557  	// type and content from the interceptor.
   558  	require.Equal(h.t, methodURI, req.MethodFullUri)
   559  	assertInterceptedType(h.t, expectedRequest, req)
   560  
   561  	// We need to accept the request.
   562  	h.sendAccept(reqIntercept.MsgId, nil)
   563  
   564  	// Now read the intercept message for the response.
   565  	respIntercept, err := h.stream.Recv()
   566  	require.NoError(h.t, err)
   567  	res := respIntercept.GetResponse()
   568  	require.NotNil(h.t, res)
   569  
   570  	// We expect the request ID to be the same for the request intercept
   571  	// and the response intercept messages. But the message IDs must be
   572  	// different/unique.
   573  	require.Equal(h.t, reqIntercept.RequestId, respIntercept.RequestId)
   574  	require.NotEqual(h.t, reqIntercept.MsgId, respIntercept.MsgId)
   575  
   576  	// We need to accept the response as well.
   577  	h.sendAccept(respIntercept.MsgId, responseReplacement)
   578  
   579  	h.responsesChan <- res
   580  }
   581  
   582  // interceptStream intercepts a streaming call, optionally requesting to replace
   583  // the (first) response sent to the client. A streaming call is expected to
   584  // receive one intercept message for the stream authentication, one for the
   585  // first request and one for the first response.
   586  //
   587  // NOTE: Must be called in a goroutine as this will block until the first
   588  // response is read from the response channel.
   589  func (h *middlewareHarness) interceptStream(methodURI string,
   590  	expectedRequest proto.Message, responseReplacement proto.Message,
   591  	readOnly bool) {
   592  
   593  	// Read intercept message and make sure it's for an RPC stream auth.
   594  	authIntercept, err := h.stream.Recv()
   595  	require.NoError(h.t, err)
   596  
   597  	// Make sure the custom condition is populated correctly (if we're using
   598  	// a macaroon with a custom condition).
   599  	if !readOnly {
   600  		require.Equal(
   601  			h.t, "itest-value", authIntercept.CustomCaveatCondition,
   602  		)
   603  	}
   604  
   605  	auth := authIntercept.GetStreamAuth()
   606  	require.NotNil(h.t, auth)
   607  
   608  	// This is just the authentication, so we can only look at the URI.
   609  	require.Equal(h.t, methodURI, auth.MethodFullUri)
   610  
   611  	// We need to accept the auth.
   612  	h.sendAccept(authIntercept.MsgId, nil)
   613  
   614  	// Read intercept message and make sure it's for an RPC request.
   615  	reqIntercept, err := h.stream.Recv()
   616  	require.NoError(h.t, err)
   617  	req := reqIntercept.GetRequest()
   618  	require.NotNil(h.t, req)
   619  
   620  	// We know the request we're going to send so make sure we get the right
   621  	// type and content from the interceptor.
   622  	require.Equal(h.t, methodURI, req.MethodFullUri)
   623  	assertInterceptedType(h.t, expectedRequest, req)
   624  
   625  	// We need to accept the request.
   626  	h.sendAccept(reqIntercept.MsgId, nil)
   627  
   628  	// Now read the intercept message for the response.
   629  	respIntercept, err := h.stream.Recv()
   630  	require.NoError(h.t, err)
   631  	res := respIntercept.GetResponse()
   632  	require.NotNil(h.t, res)
   633  
   634  	// We expect the request ID to be the same for the auth intercept,
   635  	// request intercept and the response intercept messages. But the
   636  	// message IDs must be different/unique.
   637  	require.Equal(h.t, authIntercept.RequestId, respIntercept.RequestId)
   638  	require.Equal(h.t, reqIntercept.RequestId, respIntercept.RequestId)
   639  	require.NotEqual(h.t, authIntercept.MsgId, reqIntercept.MsgId)
   640  	require.NotEqual(h.t, authIntercept.MsgId, respIntercept.MsgId)
   641  	require.NotEqual(h.t, reqIntercept.MsgId, respIntercept.MsgId)
   642  
   643  	// We need to accept the response as well.
   644  	h.sendAccept(respIntercept.MsgId, responseReplacement)
   645  
   646  	h.responsesChan <- res
   647  }
   648  
   649  // sendAccept sends an accept feedback to the RPC server.
   650  func (h *middlewareHarness) sendAccept(msgID uint64,
   651  	responseReplacement proto.Message) {
   652  
   653  	var replacementBytes []byte
   654  	if responseReplacement != nil {
   655  		var err error
   656  		replacementBytes, err = proto.Marshal(responseReplacement)
   657  		require.NoError(h.t, err)
   658  	}
   659  
   660  	err := h.stream.Send(&lnrpc.RPCMiddlewareResponse{
   661  		MiddlewareMessage: &lnrpc.RPCMiddlewareResponse_Feedback{
   662  			Feedback: &lnrpc.InterceptFeedback{
   663  				ReplaceResponse:       len(replacementBytes) > 0,
   664  				ReplacementSerialized: replacementBytes,
   665  			},
   666  		},
   667  		RefMsgId: msgID,
   668  	})
   669  	require.NoError(h.t, err)
   670  }