mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-11-19 10:37:23 +01:00
Merge pull request #6185 from sangaman/rpc-middleware-custom-caveat
rpcperms: set CustomCaveatCondition on middleware req
This commit is contained in:
@@ -92,6 +92,10 @@ Postgres](https://github.com/lightningnetwork/lnd/pull/6111)
|
|||||||
exposed](https://github.com/lightningnetwork/lnd/pull/6146) inside
|
exposed](https://github.com/lightningnetwork/lnd/pull/6146) inside
|
||||||
WaitingCloseResp from calling `PendingChannels`.
|
WaitingCloseResp from calling `PendingChannels`.
|
||||||
|
|
||||||
|
* [CustomCaveatCondition is now properly
|
||||||
|
set](https://github.com/lightningnetwork/lnd/pull/6185) on
|
||||||
|
`RPCMiddlewareRequest` messages.
|
||||||
|
|
||||||
|
|
||||||
## Routing
|
## Routing
|
||||||
|
|
||||||
@@ -108,6 +112,7 @@ Postgres](https://github.com/lightningnetwork/lnd/pull/6111)
|
|||||||
* Andras Banki-Horvath
|
* Andras Banki-Horvath
|
||||||
* Andreas Schjønhaug
|
* Andreas Schjønhaug
|
||||||
* Bjarne Magnussen
|
* Bjarne Magnussen
|
||||||
|
* Daniel McNally
|
||||||
* Elle Mouton
|
* Elle Mouton
|
||||||
* Harsha Goli
|
* Harsha Goli
|
||||||
* Joost Jager
|
* Joost Jager
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ func middlewareInterceptionTest(t *testing.T, node *lntest.HarnessNode,
|
|||||||
// block the execution of the main task otherwise.
|
// block the execution of the main task otherwise.
|
||||||
req := &lnrpc.ListChannelsRequest{ActiveOnly: true}
|
req := &lnrpc.ListChannelsRequest{ActiveOnly: true}
|
||||||
go registration.interceptUnary(
|
go registration.interceptUnary(
|
||||||
"/lnrpc.Lightning/ListChannels", req, nil,
|
"/lnrpc.Lightning/ListChannels", req, nil, readOnly,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Do the actual call now and wait for the interceptor to do its thing.
|
// Do the actual call now and wait for the interceptor to do its thing.
|
||||||
@@ -208,7 +208,7 @@ func middlewareInterceptionTest(t *testing.T, node *lntest.HarnessNode,
|
|||||||
// Let's test the same for a streaming endpoint.
|
// Let's test the same for a streaming endpoint.
|
||||||
req2 := &lnrpc.PeerEventSubscription{}
|
req2 := &lnrpc.PeerEventSubscription{}
|
||||||
go registration.interceptStream(
|
go registration.interceptStream(
|
||||||
"/lnrpc.Lightning/SubscribePeerEvents", req2, nil,
|
"/lnrpc.Lightning/SubscribePeerEvents", req2, nil, readOnly,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Do the actual call now and wait for the interceptor to do its thing.
|
// Do the actual call now and wait for the interceptor to do its thing.
|
||||||
@@ -327,6 +327,7 @@ func middlewareManipulationTest(t *testing.T, node *lntest.HarnessNode,
|
|||||||
req := &lnrpc.ListChannelsRequest{ActiveOnly: true}
|
req := &lnrpc.ListChannelsRequest{ActiveOnly: true}
|
||||||
go registration.interceptUnary(
|
go registration.interceptUnary(
|
||||||
"/lnrpc.Lightning/ListChannels", req, replacementResponse,
|
"/lnrpc.Lightning/ListChannels", req, replacementResponse,
|
||||||
|
readOnly,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Do the actual call now and wait for the interceptor to do its thing.
|
// Do the actual call now and wait for the interceptor to do its thing.
|
||||||
@@ -349,7 +350,7 @@ func middlewareManipulationTest(t *testing.T, node *lntest.HarnessNode,
|
|||||||
req2 := &lnrpc.PeerEventSubscription{}
|
req2 := &lnrpc.PeerEventSubscription{}
|
||||||
go registration.interceptStream(
|
go registration.interceptStream(
|
||||||
"/lnrpc.Lightning/SubscribePeerEvents", req2,
|
"/lnrpc.Lightning/SubscribePeerEvents", req2,
|
||||||
replacementResponse2,
|
replacementResponse2, readOnly,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Do the actual call now and wait for the interceptor to do its thing.
|
// Do the actual call now and wait for the interceptor to do its thing.
|
||||||
@@ -522,11 +523,21 @@ func registerMiddleware(t *testing.T, node *lntest.HarnessNode,
|
|||||||
// NOTE: Must be called in a goroutine as this will block until the response is
|
// NOTE: Must be called in a goroutine as this will block until the response is
|
||||||
// read from the response channel.
|
// read from the response channel.
|
||||||
func (h *middlewareHarness) interceptUnary(methodURI string,
|
func (h *middlewareHarness) interceptUnary(methodURI string,
|
||||||
expectedRequest proto.Message, responseReplacement proto.Message) {
|
expectedRequest proto.Message, responseReplacement proto.Message,
|
||||||
|
readOnly bool) {
|
||||||
|
|
||||||
// Read intercept message and make sure it's for an RPC request.
|
// Read intercept message and make sure it's for an RPC request.
|
||||||
reqIntercept, err := h.stream.Recv()
|
reqIntercept, err := h.stream.Recv()
|
||||||
require.NoError(h.t, err)
|
require.NoError(h.t, err)
|
||||||
|
|
||||||
|
// Make sure the custom condition is populated correctly (if we're using
|
||||||
|
// a macaroon with a custom condition).
|
||||||
|
if !readOnly {
|
||||||
|
require.Equal(
|
||||||
|
h.t, "itest-value", reqIntercept.CustomCaveatCondition,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
req := reqIntercept.GetRequest()
|
req := reqIntercept.GetRequest()
|
||||||
require.NotNil(h.t, req)
|
require.NotNil(h.t, req)
|
||||||
|
|
||||||
@@ -564,11 +575,21 @@ func (h *middlewareHarness) interceptUnary(methodURI string,
|
|||||||
// NOTE: Must be called in a goroutine as this will block until the first
|
// NOTE: Must be called in a goroutine as this will block until the first
|
||||||
// response is read from the response channel.
|
// response is read from the response channel.
|
||||||
func (h *middlewareHarness) interceptStream(methodURI string,
|
func (h *middlewareHarness) interceptStream(methodURI string,
|
||||||
expectedRequest proto.Message, responseReplacement proto.Message) {
|
expectedRequest proto.Message, responseReplacement proto.Message,
|
||||||
|
readOnly bool) {
|
||||||
|
|
||||||
// Read intercept message and make sure it's for an RPC stream auth.
|
// Read intercept message and make sure it's for an RPC stream auth.
|
||||||
authIntercept, err := h.stream.Recv()
|
authIntercept, err := h.stream.Recv()
|
||||||
require.NoError(h.t, err)
|
require.NoError(h.t, err)
|
||||||
|
|
||||||
|
// Make sure the custom condition is populated correctly (if we're using
|
||||||
|
// a macaroon with a custom condition).
|
||||||
|
if !readOnly {
|
||||||
|
require.Equal(
|
||||||
|
h.t, "itest-value", authIntercept.CustomCaveatCondition,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
auth := authIntercept.GetStreamAuth()
|
auth := authIntercept.GetStreamAuth()
|
||||||
require.NotNil(h.t, auth)
|
require.NotNil(h.t, auth)
|
||||||
|
|
||||||
|
|||||||
@@ -216,3 +216,37 @@ func HasCustomCaveat(mac *macaroon.Macaroon, customCaveatName string) bool {
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCustomCaveatCondition returns the custom caveat condition for the given
|
||||||
|
// custom caveat name from the given macaroon.
|
||||||
|
func GetCustomCaveatCondition(mac *macaroon.Macaroon,
|
||||||
|
customCaveatName string) string {
|
||||||
|
|
||||||
|
if mac == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
caveatPrefix := []byte(fmt.Sprintf(
|
||||||
|
"%s %s ", CondLndCustom, customCaveatName,
|
||||||
|
))
|
||||||
|
for _, caveat := range mac.Caveats() {
|
||||||
|
|
||||||
|
// The caveat id has a format of
|
||||||
|
// "lnd-custom [custom-caveat-name] [custom-caveat-condition]"
|
||||||
|
// and we only want the condition part. If we match the prefix
|
||||||
|
// part we return the condition that comes after the prefix.
|
||||||
|
if bytes.HasPrefix(caveat.Id, caveatPrefix) {
|
||||||
|
caveatSplit := strings.SplitN(
|
||||||
|
string(caveat.Id),
|
||||||
|
string(caveatPrefix),
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
if len(caveatSplit) == 2 {
|
||||||
|
return caveatSplit[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We didn't find a condition for the given custom caveat name.
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|||||||
@@ -132,6 +132,11 @@ func TestCustomConstraint(t *testing.T) {
|
|||||||
require.False(t, macaroons.HasCustomCaveat(testMacaroon, "something"))
|
require.False(t, macaroons.HasCustomCaveat(testMacaroon, "something"))
|
||||||
require.False(t, macaroons.HasCustomCaveat(nil, "foo"))
|
require.False(t, macaroons.HasCustomCaveat(nil, "foo"))
|
||||||
|
|
||||||
|
customCaveatCondition := macaroons.GetCustomCaveatCondition(
|
||||||
|
testMacaroon, "unit-test",
|
||||||
|
)
|
||||||
|
require.Equal(t, customCaveatCondition, "test-value")
|
||||||
|
|
||||||
// Custom caveats don't necessarily need a value, just the name is fine
|
// Custom caveats don't necessarily need a value, just the name is fine
|
||||||
// too to create a tagged macaroon.
|
// too to create a tagged macaroon.
|
||||||
constraintFunc = macaroons.CustomConstraint("unit-test", "")
|
constraintFunc = macaroons.CustomConstraint("unit-test", "")
|
||||||
@@ -144,4 +149,9 @@ func TestCustomConstraint(t *testing.T) {
|
|||||||
require.True(t, macaroons.HasCustomCaveat(testMacaroon, "unit-test"))
|
require.True(t, macaroons.HasCustomCaveat(testMacaroon, "unit-test"))
|
||||||
require.False(t, macaroons.HasCustomCaveat(testMacaroon, "test-value"))
|
require.False(t, macaroons.HasCustomCaveat(testMacaroon, "test-value"))
|
||||||
require.False(t, macaroons.HasCustomCaveat(testMacaroon, "something"))
|
require.False(t, macaroons.HasCustomCaveat(testMacaroon, "something"))
|
||||||
|
|
||||||
|
customCaveatCondition = macaroons.GetCustomCaveatCondition(
|
||||||
|
testMacaroon, "unit-test",
|
||||||
|
)
|
||||||
|
require.Equal(t, customCaveatCondition, "")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -910,7 +910,7 @@ func (r *InterceptorChain) middlewareRegistered() bool {
|
|||||||
|
|
||||||
// acceptRequest sends an intercept request to all middlewares that have
|
// acceptRequest sends an intercept request to all middlewares that have
|
||||||
// registered for it. This means either a middleware has requested read-only
|
// registered for it. This means either a middleware has requested read-only
|
||||||
// access or the request actually has a macaroon which a caveat the middleware
|
// access or the request actually has a macaroon with a caveat the middleware
|
||||||
// registered for.
|
// registered for.
|
||||||
func (r *InterceptorChain) acceptRequest(requestID uint64,
|
func (r *InterceptorChain) acceptRequest(requestID uint64,
|
||||||
msg *InterceptionRequest) error {
|
msg *InterceptionRequest) error {
|
||||||
@@ -929,6 +929,10 @@ func (r *InterceptorChain) acceptRequest(requestID uint64,
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
msg.CustomCaveatCondition = macaroons.GetCustomCaveatCondition(
|
||||||
|
msg.Macaroon, middleware.customCaveatName,
|
||||||
|
)
|
||||||
|
|
||||||
resp, err := middleware.intercept(requestID, msg)
|
resp, err := middleware.intercept(requestID, msg)
|
||||||
|
|
||||||
// Error during interception itself.
|
// Error during interception itself.
|
||||||
@@ -975,6 +979,10 @@ func (r *InterceptorChain) interceptResponse(ctx context.Context,
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
msg.CustomCaveatCondition = macaroons.GetCustomCaveatCondition(
|
||||||
|
msg.Macaroon, middleware.customCaveatName,
|
||||||
|
)
|
||||||
|
|
||||||
resp, err := middleware.intercept(requestID, msg)
|
resp, err := middleware.intercept(requestID, msg)
|
||||||
|
|
||||||
// Error during interception itself.
|
// Error during interception itself.
|
||||||
|
|||||||
Reference in New Issue
Block a user