• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 13035292482

29 Jan 2025 03:59PM UTC coverage: 49.3% (-9.5%) from 58.777%
13035292482

Pull #9456

github

mohamedawnallah
docs: update release-notes-0.19.0.md

In this commit, we warn users about the removal
of RPCs `SendToRoute`, `SendToRouteSync`, `SendPayment`,
and `SendPaymentSync` in the next release 0.20.
Pull Request #9456: lnrpc+docs: deprecate warning `SendToRoute`, `SendToRouteSync`, `SendPayment`, and `SendPaymentSync` in Release 0.19

100634 of 204126 relevant lines covered (49.3%)

1.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

70.91
/macaroons/service.go
1
package macaroons
2

3
import (
4
        "context"
5
        "encoding/hex"
6
        "fmt"
7

8
        "google.golang.org/grpc/metadata"
9
        "gopkg.in/macaroon-bakery.v2/bakery"
10
        "gopkg.in/macaroon-bakery.v2/bakery/checkers"
11
        macaroon "gopkg.in/macaroon.v2"
12
)
13

14
var (
15
        // ErrMissingRootKeyID specifies the root key ID is missing.
16
        ErrMissingRootKeyID = fmt.Errorf("missing root key ID")
17

18
        // ErrDeletionForbidden is used when attempting to delete the
19
        // DefaultRootKeyID or the encryptedKeyID.
20
        ErrDeletionForbidden = fmt.Errorf("the specified ID cannot be deleted")
21

22
        // PermissionEntityCustomURI is a special entity name for a permission
23
        // that does not describe an entity:action pair but instead specifies a
24
        // specific URI that needs to be granted access to. This can be used for
25
        // more fine-grained permissions where a macaroon only grants access to
26
        // certain methods instead of a whole list of methods that define the
27
        // same entity:action pairs. For example: uri:/lnrpc.Lightning/GetInfo
28
        // only gives access to the GetInfo call.
29
        PermissionEntityCustomURI = "uri"
30

31
        // ErrUnknownVersion is returned when a macaroon is of an unknown
32
        // is presented.
33
        ErrUnknownVersion = fmt.Errorf("unknown macaroon version")
34

35
        // ErrInvalidID is returned when a macaroon ID is invalid.
36
        ErrInvalidID = fmt.Errorf("invalid ID")
37
)
38

39
// MacaroonValidator is an interface type that can check if macaroons are valid.
40
type MacaroonValidator interface {
41
        // ValidateMacaroon extracts the macaroon from the context's gRPC
42
        // metadata, checks its signature, makes sure all specified permissions
43
        // for the called method are contained within and finally ensures all
44
        // caveat conditions are met. A non-nil error is returned if any of the
45
        // checks fail.
46
        ValidateMacaroon(ctx context.Context,
47
                requiredPermissions []bakery.Op, fullMethod string) error
48
}
49

50
// ExtendedRootKeyStore is an interface augments the existing
51
// macaroons.RootKeyStorage interface by adding a number of additional utility
52
// methods such as encrypting and decrypting the root key given a password.
53
type ExtendedRootKeyStore interface {
54
        bakery.RootKeyStore
55

56
        // Close closes the RKS and zeros out any in-memory encryption keys.
57
        Close() error
58

59
        // CreateUnlock calls the underlying root key store's CreateUnlock and
60
        // returns the result.
61
        CreateUnlock(password *[]byte) error
62

63
        // ListMacaroonIDs returns all the root key ID values except the value
64
        // of encryptedKeyID.
65
        ListMacaroonIDs(ctxt context.Context) ([][]byte, error)
66

67
        // DeleteMacaroonID removes one specific root key ID. If the root key
68
        // ID is found and deleted, it will be returned.
69
        DeleteMacaroonID(ctxt context.Context, rootKeyID []byte) ([]byte, error)
70

71
        // ChangePassword calls the underlying root key store's ChangePassword
72
        // and returns the result.
73
        ChangePassword(oldPw, newPw []byte) error
74

75
        // GenerateNewRootKey calls the underlying root key store's
76
        // GenerateNewRootKey and returns the result.
77
        GenerateNewRootKey() error
78

79
        // SetRootKey calls the underlying root key store's SetRootKey and
80
        // returns the result.
81
        SetRootKey(rootKey []byte) error
82
}
83

84
// Service encapsulates bakery.Bakery and adds a Close() method that zeroes the
85
// root key service encryption keys, as well as utility methods to validate a
86
// macaroon against the bakery and gRPC middleware for macaroon-based auth.
87
type Service struct {
88
        bakery.Bakery
89

90
        rks bakery.RootKeyStore
91

92
        // ExternalValidators is a map between an absolute gRPC URIs and the
93
        // corresponding external macaroon validator to be used for that URI.
94
        // If no external validator for an URI is specified, the service will
95
        // use the internal validator.
96
        ExternalValidators map[string]MacaroonValidator
97

98
        // StatelessInit denotes if the service was initialized in the stateless
99
        // mode where no macaroon files should be created on disk.
100
        StatelessInit bool
101
}
102

103
// NewService returns a service backed by the macaroon DB backend. The `checks`
104
// argument can be any of the `Checker` type functions defined in this package,
105
// or a custom checker if desired. This constructor prevents double-registration
106
// of checkers to prevent panics, so listing the same checker more than once is
107
// not harmful. Default checkers, such as those for `allow`, `time-before`,
108
// `declared`, and `error` caveats are registered automatically and don't need
109
// to be added.
110
func NewService(keyStore bakery.RootKeyStore, location string,
111
        statelessInit bool, checks ...Checker) (*Service, error) {
3✔
112

3✔
113
        macaroonParams := bakery.BakeryParams{
3✔
114
                Location:     location,
3✔
115
                RootKeyStore: keyStore,
3✔
116
                // No third-party caveat support for now.
3✔
117
                // TODO(aakselrod): Add third-party caveat support.
3✔
118
                Locator: nil,
3✔
119
                Key:     nil,
3✔
120
        }
3✔
121

3✔
122
        svc := bakery.New(macaroonParams)
3✔
123

3✔
124
        // Register all custom caveat checkers with the bakery's checker.
3✔
125
        // TODO(aakselrod): Add more checks as required.
3✔
126
        checker := svc.Checker.FirstPartyCaveatChecker.(*checkers.Checker)
3✔
127
        for _, check := range checks {
6✔
128
                cond, fun := check()
3✔
129
                if !isRegistered(checker, cond) {
6✔
130
                        checker.Register(cond, "std", fun)
3✔
131
                }
3✔
132
        }
133

134
        return &Service{
3✔
135
                Bakery:             *svc,
3✔
136
                rks:                keyStore,
3✔
137
                ExternalValidators: make(map[string]MacaroonValidator),
3✔
138
                StatelessInit:      statelessInit,
3✔
139
        }, nil
3✔
140
}
141

142
// isRegistered checks to see if the required checker has already been
143
// registered in order to avoid a panic caused by double registration.
144
func isRegistered(c *checkers.Checker, name string) bool {
3✔
145
        if c == nil {
3✔
146
                return false
×
147
        }
×
148

149
        for _, info := range c.Info() {
6✔
150
                if info.Name == name &&
3✔
151
                        info.Prefix == "" &&
3✔
152
                        info.Namespace == "std" {
3✔
153
                        return true
×
154
                }
×
155
        }
156

157
        return false
3✔
158
}
159

160
// RegisterExternalValidator registers a custom, external macaroon validator for
161
// the specified absolute gRPC URI. That validator is then fully responsible to
162
// make sure any macaroon passed for a request to that URI is valid and
163
// satisfies all conditions.
164
func (svc *Service) RegisterExternalValidator(fullMethod string,
165
        validator MacaroonValidator) error {
×
166

×
167
        if validator == nil {
×
168
                return fmt.Errorf("validator cannot be nil")
×
169
        }
×
170

171
        _, ok := svc.ExternalValidators[fullMethod]
×
172
        if ok {
×
173
                return fmt.Errorf("external validator for method %s already "+
×
174
                        "registered", fullMethod)
×
175
        }
×
176

177
        svc.ExternalValidators[fullMethod] = validator
×
178
        return nil
×
179
}
180

181
// ValidateMacaroon validates the capabilities of a given request given a
182
// bakery service, context, and uri. Within the passed context.Context, we
183
// expect a macaroon to be encoded as request metadata using the key
184
// "macaroon".
185
func (svc *Service) ValidateMacaroon(ctx context.Context,
186
        requiredPermissions []bakery.Op, fullMethod string) error {
3✔
187

3✔
188
        // Get macaroon bytes from context and unmarshal into macaroon.
3✔
189
        macHex, err := RawMacaroonFromContext(ctx)
3✔
190
        if err != nil {
6✔
191
                return err
3✔
192
        }
3✔
193

194
        // With the macaroon obtained, we'll now decode the hex-string encoding.
195
        macBytes, err := hex.DecodeString(macHex)
3✔
196
        if err != nil {
3✔
197
                return err
×
198
        }
×
199

200
        return svc.CheckMacAuth(
3✔
201
                ctx, macBytes, requiredPermissions, fullMethod,
3✔
202
        )
3✔
203
}
204

205
// CheckMacAuth checks that the macaroon is not disobeying any caveats and is
206
// authorized to perform the operation the user wants to perform.
207
func (svc *Service) CheckMacAuth(ctx context.Context, macBytes []byte,
208
        requiredPermissions []bakery.Op, fullMethod string) error {
3✔
209

3✔
210
        // With the macaroon obtained, we'll now unmarshal it from binary into
3✔
211
        // its concrete struct representation.
3✔
212
        mac := &macaroon.Macaroon{}
3✔
213
        err := mac.UnmarshalBinary(macBytes)
3✔
214
        if err != nil {
3✔
215
                return err
×
216
        }
×
217

218
        // Ensure that the macaroon is using the exact same version as we
219
        // expect. In the future, we can relax this check to phase in new
220
        // versions.
221
        if mac.Version() != macaroon.V2 {
3✔
222
                return fmt.Errorf("%w: %v", ErrUnknownVersion,
×
223
                        mac.Version())
×
224
        }
×
225

226
        // Run a similar version check on the ID used for the macaroon as well.
227
        const minIDLength = 1
3✔
228
        if len(mac.Id()) < minIDLength {
3✔
229
                return ErrInvalidID
×
230
        }
×
231
        if mac.Id()[0] != byte(bakery.Version3) {
6✔
232
                return ErrInvalidID
3✔
233
        }
3✔
234

235
        // Check the method being called against the permitted operation, the
236
        // expiration time and IP address and return the result.
237
        authChecker := svc.Checker.Auth(macaroon.Slice{mac})
3✔
238
        _, err = authChecker.Allow(ctx, requiredPermissions...)
3✔
239

3✔
240
        // If the macaroon contains broad permissions and checks out, we're
3✔
241
        // done.
3✔
242
        if err == nil {
6✔
243
                return nil
3✔
244
        }
3✔
245

246
        // To also allow the special permission of "uri:<FullMethod>" to be a
247
        // valid permission, we need to check it manually in case there is no
248
        // broader scope permission defined.
249
        _, err = authChecker.Allow(ctx, bakery.Op{
3✔
250
                Entity: PermissionEntityCustomURI,
3✔
251
                Action: fullMethod,
3✔
252
        })
3✔
253
        return err
3✔
254
}
255

256
// Close closes the database that underlies the RootKeyStore and zeroes the
257
// encryption keys.
258
func (svc *Service) Close() error {
3✔
259
        if boltRKS, ok := svc.rks.(ExtendedRootKeyStore); ok {
6✔
260
                return boltRKS.Close()
3✔
261
        }
3✔
262

263
        return nil
×
264
}
265

266
// CreateUnlock calls the underlying root key store's CreateUnlock and returns
267
// the result.
268
func (svc *Service) CreateUnlock(password *[]byte) error {
3✔
269
        if boltRKS, ok := svc.rks.(ExtendedRootKeyStore); ok {
6✔
270
                return boltRKS.CreateUnlock(password)
3✔
271
        }
3✔
272

273
        return nil
×
274
}
275

276
// NewMacaroon wraps around the function Oven.NewMacaroon with the defaults,
277
//   - version is always bakery.LatestVersion;
278
//   - caveats is always nil.
279
//
280
// In addition, it takes a rootKeyID parameter, and puts it into the context.
281
// The context is passed through Oven.NewMacaroon(), in which calls the function
282
// RootKey(), that reads the context for rootKeyID.
283
func (svc *Service) NewMacaroon(
284
        ctx context.Context, rootKeyID []byte,
285
        ops ...bakery.Op) (*bakery.Macaroon, error) {
3✔
286

3✔
287
        // Check rootKeyID is not called with nil or empty bytes. We want the
3✔
288
        // caller to be aware the value of root key ID used, so we won't replace
3✔
289
        // it with the DefaultRootKeyID if not specified.
3✔
290
        if len(rootKeyID) == 0 {
3✔
291
                return nil, ErrMissingRootKeyID
×
292
        }
×
293

294
        // Pass the root key ID to context.
295
        ctx = ContextWithRootKeyID(ctx, rootKeyID)
3✔
296

3✔
297
        return svc.Oven.NewMacaroon(ctx, bakery.LatestVersion, nil, ops...)
3✔
298
}
299

300
// ListMacaroonIDs returns all the root key ID values except the value of
301
// encryptedKeyID.
302
func (svc *Service) ListMacaroonIDs(ctxt context.Context) ([][]byte, error) {
3✔
303
        if boltRKS, ok := svc.rks.(ExtendedRootKeyStore); ok {
6✔
304
                return boltRKS.ListMacaroonIDs(ctxt)
3✔
305
        }
3✔
306

307
        return nil, nil
×
308
}
309

310
// DeleteMacaroonID removes one specific root key ID. If the root key ID is
311
// found and deleted, it will be returned.
312
func (svc *Service) DeleteMacaroonID(ctxt context.Context,
313
        rootKeyID []byte) ([]byte, error) {
3✔
314

3✔
315
        if boltRKS, ok := svc.rks.(ExtendedRootKeyStore); ok {
6✔
316
                return boltRKS.DeleteMacaroonID(ctxt, rootKeyID)
3✔
317
        }
3✔
318

319
        return nil, nil
×
320
}
321

322
// GenerateNewRootKey calls the underlying root key store's GenerateNewRootKey
323
// and returns the result.
324
func (svc *Service) GenerateNewRootKey() error {
×
325
        if boltRKS, ok := svc.rks.(ExtendedRootKeyStore); ok {
×
326
                return boltRKS.GenerateNewRootKey()
×
327
        }
×
328

329
        return nil
×
330
}
331

332
// SetRootKey calls the underlying root key store's SetRootKey and returns the
333
// result.
334
func (svc *Service) SetRootKey(rootKey []byte) error {
×
335
        if boltRKS, ok := svc.rks.(ExtendedRootKeyStore); ok {
×
336
                return boltRKS.SetRootKey(rootKey)
×
337
        }
×
338

339
        return nil
×
340
}
341

342
// ChangePassword calls the underlying root key store's ChangePassword and
343
// returns the result.
344
func (svc *Service) ChangePassword(oldPw, newPw []byte) error {
3✔
345
        if boltRKS, ok := svc.rks.(ExtendedRootKeyStore); ok {
6✔
346
                return boltRKS.ChangePassword(oldPw, newPw)
3✔
347
        }
3✔
348

349
        return nil
×
350
}
351

352
// RawMacaroonFromContext is a helper function that extracts a raw macaroon
353
// from the given incoming gRPC request context.
354
func RawMacaroonFromContext(ctx context.Context) (string, error) {
3✔
355
        // Get macaroon bytes from context and unmarshal into macaroon.
3✔
356
        md, ok := metadata.FromIncomingContext(ctx)
3✔
357
        if !ok {
3✔
358
                return "", fmt.Errorf("unable to get metadata from context")
×
359
        }
×
360
        if len(md["macaroon"]) != 1 {
6✔
361
                return "", fmt.Errorf("expected 1 macaroon, got %d",
3✔
362
                        len(md["macaroon"]))
3✔
363
        }
3✔
364

365
        return md["macaroon"][0], nil
3✔
366
}
367

368
// SafeCopyMacaroon creates a copy of a macaroon that is safe to be used and
369
// modified. This is necessary because the macaroon library's own Clone() method
370
// is unsafe for certain edge cases, resulting in both the cloned and the
371
// original macaroons to be modified.
372
func SafeCopyMacaroon(mac *macaroon.Macaroon) (*macaroon.Macaroon, error) {
3✔
373
        macBytes, err := mac.MarshalBinary()
3✔
374
        if err != nil {
3✔
375
                return nil, err
×
376
        }
×
377

378
        newMac := &macaroon.Macaroon{}
3✔
379
        if err := newMac.UnmarshalBinary(macBytes); err != nil {
3✔
380
                return nil, err
×
381
        }
×
382

383
        return newMac, nil
3✔
384
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc