11package accounts
22
33import (
4+ "bytes"
45 "context"
56 "encoding/hex"
67 "errors"
78 "fmt"
9+ "strings"
810
911 mid "github.com/lightninglabs/lightning-terminal/rpcmiddleware"
12+ "github.com/lightningnetwork/lnd/fn"
1013 "github.com/lightningnetwork/lnd/lnrpc"
1114 "github.com/lightningnetwork/lnd/macaroons"
1215 "google.golang.org/protobuf/proto"
16+ "gopkg.in/macaroon-bakery.v2/bakery/checkers"
1317 "gopkg.in/macaroon.v2"
1418)
1519
@@ -23,6 +27,15 @@ const (
2327 accountMiddlewareName = "lit-account"
2428)
2529
30+ var (
31+ // caveatPrefix is the prefix that is used for custom caveats that are
32+ // used by the account system. This prefix is used to identify the
33+ // custom caveat and extract the condition (the AccountID) from it.
34+ caveatPrefix = []byte (fmt .Sprintf (
35+ "%s %s " , macaroons .CondLndCustom , CondAccount ,
36+ ))
37+ )
38+
2639// Name returns the name of the interceptor.
2740func (s * InterceptorService ) Name () string {
2841 return accountMiddlewareName
@@ -199,22 +212,68 @@ func parseRPCMessage(msg *lnrpc.RPCMessage) (proto.Message, error) {
199212// accountFromMacaroon attempts to extract an account ID from the custom account
200213// caveat in the macaroon.
201214func accountFromMacaroon (mac * macaroon.Macaroon ) (* AccountID , error ) {
202- // Extract the account caveat from the macaroon.
203- macaroonAccount := macaroons .GetCustomCaveatCondition (mac , CondAccount )
204- if macaroonAccount == "" {
205- // There is no condition that locks the macaroon to an account,
206- // so there is nothing to check.
215+ if mac == nil {
207216 return nil , nil
208217 }
209218
210- // The macaroon is indeed locked to an account. Fetch the account and
211- // validate its balance.
212- accountIDBytes , err := hex .DecodeString (macaroonAccount )
219+ // Extract the account caveat from the macaroon.
220+ accountID , err := IDFromCaveats (mac .Caveats ())
213221 if err != nil {
214222 return nil , err
215223 }
216224
225+ var id * AccountID
226+ accountID .WhenSome (func (aID AccountID ) {
227+ id = & aID
228+ })
229+
230+ return id , nil
231+ }
232+
233+ // CaveatFromID creates a custom caveat that can be used to bind a macaroon to
234+ // a certain account.
235+ func CaveatFromID (id AccountID ) macaroon.Caveat {
236+ condition := checkers .Condition (macaroons .CondLndCustom , fmt .Sprintf (
237+ "%s %x" , CondAccount , id [:],
238+ ))
239+
240+ return macaroon.Caveat {Id : []byte (condition )}
241+ }
242+
243+ // IDFromCaveats attempts to extract an AccountID from the given set of caveats
244+ // by looking for the custom caveat that binds a macaroon to a certain account.
245+ func IDFromCaveats (caveats []macaroon.Caveat ) (fn.Option [AccountID ], error ) {
246+ var accountIDStr string
247+ for _ , caveat := range caveats {
248+ // The caveat id has a format of
249+ // "lnd-custom [custom-caveat-name] [custom-caveat-condition]"
250+ // and we only want the condition part. If we match the prefix
251+ // part we return the condition that comes after the prefix.
252+ if bytes .HasPrefix (caveat .Id , caveatPrefix ) {
253+ caveatSplit := strings .SplitN (
254+ string (caveat .Id ),
255+ string (caveatPrefix ),
256+ 2 ,
257+ )
258+ if len (caveatSplit ) == 2 {
259+ accountIDStr = caveatSplit [1 ]
260+
261+ break
262+ }
263+ }
264+ }
265+
266+ if accountIDStr == "" {
267+ return fn .None [AccountID ](), nil
268+ }
269+
217270 var accountID AccountID
271+ accountIDBytes , err := hex .DecodeString (accountIDStr )
272+ if err != nil {
273+ return fn .None [AccountID ](), err
274+ }
275+
218276 copy (accountID [:], accountIDBytes )
219- return & accountID , nil
277+
278+ return fn .Some (accountID ), nil
220279}
0 commit comments