diff --git a/README.md b/README.md index 8af71e0..8ed056a 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ non-custodial on/off ramp for the Lightning Network. Aperture is a HTTP 402 reverse proxy that supports proxying requests for gRPC (HTTP/2) and REST (HTTP/1 and HTTP/2) backends using the [L402 Protocol -Standard](https://lsat.tech/). L402 is short for: the Lightning HTTP 402 +Standard][l402]. L402 is short for: the Lightning HTTP 402 protocol. L402 combines HTTP 402, macaroons, and the Lightning Network to create a new standard for authentication and paid services on the web. @@ -23,6 +23,8 @@ novel constructs such as automated tier upgrades. In another light, this can be viewed as a global HTTP 402 reverse proxy at the load balancing level for web services and APIs. +[l402]: https://github.com/lightninglabs/L402 + ## Installation / Setup **lnd** diff --git a/aperture.go b/aperture.go index 5fbb4dd..57eaa71 100644 --- a/aperture.go +++ b/aperture.go @@ -47,7 +47,7 @@ import ( const ( // topLevelKey is the top level key for an etcd cluster where we'll - // store all LSAT proxy related data. + // store all L402 proxy related data. topLevelKey = "lsat/proxy" // etcdKeyDelimeter is the delimeter we'll use for all etcd keys to @@ -311,7 +311,7 @@ func (a *Aperture) Start(errChan chan error) error { authCfg := a.cfg.Authenticator genInvoiceReq := func(price int64) (*lnrpc.Invoice, error) { return &lnrpc.Invoice{ - Memo: "LSAT", + Memo: "L402", Value: price, }, nil } @@ -819,7 +819,7 @@ func createProxy(cfg *Config, challenger challenger.Challenger, ServiceLimiter: newStaticServiceLimiter(cfg.Services), Now: time.Now, }) - authenticator := auth.NewLsatAuthenticator(minter, challenger) + authenticator := auth.NewL402Authenticator(minter, challenger) // By default the static file server only returns 404 answers for // security reasons. Serving files from the staticRoot directory has to diff --git a/aperturedb/secrets.go b/aperturedb/secrets.go index e698cca..9c956c9 100644 --- a/aperturedb/secrets.go +++ b/aperturedb/secrets.go @@ -8,7 +8,7 @@ import ( "fmt" "github.com/lightninglabs/aperture/aperturedb/sqlc" - "github.com/lightninglabs/aperture/lsat" + "github.com/lightninglabs/aperture/l402" "github.com/lightninglabs/aperture/mint" "github.com/lightningnetwork/lnd/clock" ) @@ -81,11 +81,11 @@ func NewSecretsStore(db BatchedSecretsDB) *SecretsStore { // NewSecret creates a new cryptographically random secret which is // keyed by the given hash. func (s *SecretsStore) NewSecret(ctx context.Context, - hash [sha256.Size]byte) ([lsat.SecretSize]byte, error) { + hash [sha256.Size]byte) ([l402.SecretSize]byte, error) { - var secret [lsat.SecretSize]byte + var secret [l402.SecretSize]byte if _, err := rand.Read(secret[:]); err != nil { - return [lsat.SecretSize]byte{}, err + return [l402.SecretSize]byte{}, err } var writeTxOpts SecretsDBTxOptions @@ -103,7 +103,7 @@ func (s *SecretsStore) NewSecret(ctx context.Context, }) if err != nil { - return [lsat.SecretSize]byte{}, fmt.Errorf("unable to insert "+ + return [l402.SecretSize]byte{}, fmt.Errorf("unable to insert "+ "new secret for hash(%x): %w", hash, err) } @@ -114,9 +114,9 @@ func (s *SecretsStore) NewSecret(ctx context.Context, // corresponds to the given hash. If there is no secret, then // ErrSecretNotFound is returned. func (s *SecretsStore) GetSecret(ctx context.Context, - hash [sha256.Size]byte) ([lsat.SecretSize]byte, error) { + hash [sha256.Size]byte) ([l402.SecretSize]byte, error) { - var secret [lsat.SecretSize]byte + var secret [l402.SecretSize]byte readOpts := NewSecretsDBReadTx() err := s.db.ExecTx(ctx, &readOpts, func(db SecretsDB) error { secretRow, err := db.GetSecretByHash(ctx, hash[:]) @@ -134,7 +134,7 @@ func (s *SecretsStore) GetSecret(ctx context.Context, }) if err != nil { - return [lsat.SecretSize]byte{}, fmt.Errorf("unable to get "+ + return [l402.SecretSize]byte{}, fmt.Errorf("unable to get "+ "secret for hash(%x): %w", hash, err) } diff --git a/auth/authenticator.go b/auth/authenticator.go index d43c17a..5aef553 100644 --- a/auth/authenticator.go +++ b/auth/authenticator.go @@ -6,28 +6,28 @@ import ( "fmt" "net/http" - "github.com/lightninglabs/aperture/lsat" + "github.com/lightninglabs/aperture/l402" "github.com/lightninglabs/aperture/mint" "github.com/lightningnetwork/lnd/lnrpc" ) -// LsatAuthenticator is an authenticator that uses the LSAT protocol to +// L402Authenticator is an authenticator that uses the L402 protocol to // authenticate requests. -type LsatAuthenticator struct { +type L402Authenticator struct { minter Minter checker InvoiceChecker } -// A compile time flag to ensure the LsatAuthenticator satisfies the +// A compile time flag to ensure the L402Authenticator satisfies the // Authenticator interface. -var _ Authenticator = (*LsatAuthenticator)(nil) +var _ Authenticator = (*L402Authenticator)(nil) -// NewLsatAuthenticator creates a new authenticator that authenticates requests -// based on LSAT tokens. -func NewLsatAuthenticator(minter Minter, - checker InvoiceChecker) *LsatAuthenticator { +// NewL402Authenticator creates a new authenticator that authenticates requests +// based on L402 tokens. +func NewL402Authenticator(minter Minter, + checker InvoiceChecker) *L402Authenticator { - return &LsatAuthenticator{ + return &L402Authenticator{ minter: minter, checker: checker, } @@ -37,11 +37,11 @@ func NewLsatAuthenticator(minter Minter, // to a given backend service. // // NOTE: This is part of the Authenticator interface. -func (l *LsatAuthenticator) Accept(header *http.Header, serviceName string) bool { +func (l *L402Authenticator) Accept(header *http.Header, serviceName string) bool { // Try reading the macaroon and preimage from the HTTP header. This can // be in different header fields depending on the implementation and/or // protocol. - mac, preimage, err := lsat.FromHeader(header) + mac, preimage, err := l402.FromHeader(header) if err != nil { log.Debugf("Deny: %v", err) return false @@ -52,9 +52,9 @@ func (l *LsatAuthenticator) Accept(header *http.Header, serviceName string) bool Preimage: preimage, TargetService: serviceName, } - err = l.minter.VerifyLSAT(context.Background(), verificationParams) + err = l.minter.VerifyL402(context.Background(), verificationParams) if err != nil { - log.Debugf("Deny: LSAT validation failed: %v", err) + log.Debugf("Deny: L402 validation failed: %v", err) return false } @@ -71,35 +71,54 @@ func (l *LsatAuthenticator) Accept(header *http.Header, serviceName string) bool return true } +const ( + // lsatAuthScheme is an outdated RFC 7235 auth-scheme used by aperture. + lsatAuthScheme = "LSAT" + + // l402AuthScheme is the current RFC 7235 auth-scheme used by aperture. + l402AuthScheme = "L402" +) + // FreshChallengeHeader returns a header containing a challenge for the user to // complete. // // NOTE: This is part of the Authenticator interface. -func (l *LsatAuthenticator) FreshChallengeHeader(r *http.Request, +func (l *L402Authenticator) FreshChallengeHeader(r *http.Request, serviceName string, servicePrice int64) (http.Header, error) { - service := lsat.Service{ + service := l402.Service{ Name: serviceName, - Tier: lsat.BaseTier, + Tier: l402.BaseTier, Price: servicePrice, } - mac, paymentRequest, err := l.minter.MintLSAT( + mac, paymentRequest, err := l.minter.MintL402( context.Background(), service, ) if err != nil { - log.Errorf("Error minting LSAT: %v", err) + log.Errorf("Error minting L402: %v", err) return nil, err } macBytes, err := mac.MarshalBinary() if err != nil { - log.Errorf("Error serializing LSAT: %v", err) + log.Errorf("Error serializing L402: %v", err) } - str := fmt.Sprintf("LSAT macaroon=\"%s\", invoice=\"%s\"", + header := http.Header{ + "Content-Type": []string{"application/grpc"}, + } + + str := fmt.Sprintf("macaroon=\"%s\", invoice=\"%s\"", base64.StdEncoding.EncodeToString(macBytes), paymentRequest) - header := r.Header - header.Set("WWW-Authenticate", str) - log.Debugf("Created new challenge header: [%s]", str) + // Old loop software (via ClientInterceptor code of aperture) looks + // for "LSAT" in the first instance of WWW-Authenticate header, so + // legacy header must go first not to break backward compatibility. + lsatValue := lsatAuthScheme + " " + str + l402Value := l402AuthScheme + " " + str + header.Set("WWW-Authenticate", lsatValue) + log.Debugf("Created new challenge header: [%s]", lsatValue) + header.Add("WWW-Authenticate", l402Value) + log.Debugf("Created new challenge header: [%s]", l402Value) + return header, nil } diff --git a/auth/authenticator_test.go b/auth/authenticator_test.go index efd3629..afaf056 100644 --- a/auth/authenticator_test.go +++ b/auth/authenticator_test.go @@ -8,7 +8,7 @@ import ( "testing" "github.com/lightninglabs/aperture/auth" - "github.com/lightninglabs/aperture/lsat" + "github.com/lightninglabs/aperture/l402" "gopkg.in/macaroon.v2" ) @@ -21,8 +21,8 @@ func createDummyMacHex(preimage string) string { if err != nil { panic(err) } - preimageCaveat := lsat.Caveat{Condition: lsat.PreimageKey, Value: preimage} - err = lsat.AddFirstPartyCaveats(dummyMac, preimageCaveat) + preimageCaveat := l402.Caveat{Condition: l402.PreimageKey, Value: preimage} + err = l402.AddFirstPartyCaveats(dummyMac, preimageCaveat) if err != nil { panic(err) } @@ -33,9 +33,9 @@ func createDummyMacHex(preimage string) string { return hex.EncodeToString(macBytes) } -// TestLsatAuthenticator tests that the authenticator properly handles auth +// TestL402Authenticator tests that the authenticator properly handles auth // headers and the tokens contained in them. -func TestLsatAuthenticator(t *testing.T) { +func TestL402Authenticator(t *testing.T) { var ( testPreimage = "49349dfea4abed3cd14f6d356afa83de" + "9787b609f088c8df09bacc7b4bd21b39" @@ -65,21 +65,21 @@ func TestLsatAuthenticator(t *testing.T) { { id: "empty auth header", header: &http.Header{ - lsat.HeaderAuthorization: []string{}, + l402.HeaderAuthorization: []string{}, }, result: false, }, { id: "zero length auth header", header: &http.Header{ - lsat.HeaderAuthorization: []string{""}, + l402.HeaderAuthorization: []string{""}, }, result: false, }, { id: "invalid auth header", header: &http.Header{ - lsat.HeaderAuthorization: []string{ + l402.HeaderAuthorization: []string{ "foo", }, }, @@ -88,23 +88,45 @@ func TestLsatAuthenticator(t *testing.T) { { id: "invalid macaroon metadata header", header: &http.Header{ - lsat.HeaderMacaroonMD: []string{"foo"}, + l402.HeaderMacaroonMD: []string{"foo"}, }, result: false, }, { id: "invalid macaroon header", header: &http.Header{ - lsat.HeaderMacaroon: []string{"foo"}, + l402.HeaderMacaroon: []string{"foo"}, }, result: false, }, { - id: "valid auth header", + id: "valid auth header (both LSAT and L402)", header: &http.Header{ - lsat.HeaderAuthorization: []string{ + l402.HeaderAuthorization: []string{ "LSAT " + testMacBase64 + ":" + testPreimage, + "L402 " + testMacBase64 + ":" + + testPreimage, + }, + }, + result: true, + }, + { + id: "valid auth header (LSAT only)", + header: &http.Header{ + l402.HeaderAuthorization: []string{ + "LSAT " + testMacBase64 + ":" + + testPreimage, + }, + }, + result: true, + }, + { + id: "valid auth header (L402 only)", + header: &http.Header{ + l402.HeaderAuthorization: []string{ + "L402 " + testMacBase64 + ":" + + testPreimage, }, }, result: true, @@ -112,7 +134,7 @@ func TestLsatAuthenticator(t *testing.T) { { id: "valid macaroon metadata header", header: &http.Header{ - lsat.HeaderMacaroonMD: []string{ + l402.HeaderMacaroonMD: []string{ testMacHex, }}, result: true, @@ -120,7 +142,7 @@ func TestLsatAuthenticator(t *testing.T) { { id: "valid macaroon header", header: &http.Header{ - lsat.HeaderMacaroon: []string{ + l402.HeaderMacaroon: []string{ testMacHex, }, }, @@ -129,7 +151,7 @@ func TestLsatAuthenticator(t *testing.T) { { id: "valid macaroon header, wrong invoice state", header: &http.Header{ - lsat.HeaderMacaroon: []string{ + l402.HeaderMacaroon: []string{ testMacHex, }, }, @@ -140,7 +162,7 @@ func TestLsatAuthenticator(t *testing.T) { ) c := &mockChecker{} - a := auth.NewLsatAuthenticator(&mockMint{}, c) + a := auth.NewL402Authenticator(&mockMint{}, c) for _, testCase := range headerTests { c.err = testCase.checkErr result := a.Accept(testCase.header, "test") diff --git a/auth/interface.go b/auth/interface.go index 08551ab..7b51dcc 100644 --- a/auth/interface.go +++ b/auth/interface.go @@ -5,7 +5,7 @@ import ( "net/http" "time" - "github.com/lightninglabs/aperture/lsat" + "github.com/lightninglabs/aperture/l402" "github.com/lightninglabs/aperture/mint" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lntypes" @@ -30,14 +30,14 @@ type Authenticator interface { FreshChallengeHeader(*http.Request, string, int64) (http.Header, error) } -// Minter is an entity that is able to mint and verify LSATs for a set of +// Minter is an entity that is able to mint and verify L402s for a set of // services. type Minter interface { - // MintLSAT mints a new LSAT for the target services. - MintLSAT(context.Context, ...lsat.Service) (*macaroon.Macaroon, string, error) + // MintL402 mints a new L402 for the target services. + MintL402(context.Context, ...l402.Service) (*macaroon.Macaroon, string, error) - // VerifyLSAT attempts to verify an LSAT with the given parameters. - VerifyLSAT(context.Context, *mint.VerificationParams) error + // VerifyL402 attempts to verify an L402 with the given parameters. + VerifyL402(context.Context, *mint.VerificationParams) error } // InvoiceChecker is an entity that is able to check the status of an invoice, diff --git a/auth/mock_authenticator.go b/auth/mock_authenticator.go index e52ff17..7f4c380 100644 --- a/auth/mock_authenticator.go +++ b/auth/mock_authenticator.go @@ -34,14 +34,19 @@ func (a MockAuthenticator) Accept(header *http.Header, _ string) bool { func (a MockAuthenticator) FreshChallengeHeader(r *http.Request, _ string, _ int64) (http.Header, error) { - header := r.Header - header.Set( - "WWW-Authenticate", "LSAT macaroon=\"AGIAJEemVQUTEyNCR0exk7ek9"+ - "0Cg==\", invoice=\"lnbc1500n1pw5kjhmpp5fu6xhthlt2vucm"+ - "zkx6c7wtlh2r625r30cyjsfqhu8rsx4xpz5lwqdpa2fjkzep6yptk"+ - "sct5yp5hxgrrv96hx6twvusycn3qv9jx7ur5d9hkugr5dusx6cqzp"+ - "gxqr23s79ruapxc4j5uskt4htly2salw4drq979d7rcela9wz02el"+ - "hypmdzmzlnxuknpgfyfm86pntt8vvkvffma5qc9n50h4mvqhngadq"+ - "y3ngqjcym5a\"") + header := http.Header{ + "Content-Type": []string{"application/grpc"}, + } + + str := "macaroon=\"AGIAJEemVQUTEyNCR0exk7ek9" + + "0Cg==\", invoice=\"lnbc1500n1pw5kjhmpp5fu6xhthlt2vucm" + + "zkx6c7wtlh2r625r30cyjsfqhu8rsx4xpz5lwqdpa2fjkzep6yptk" + + "sct5yp5hxgrrv96hx6twvusycn3qv9jx7ur5d9hkugr5dusx6cqzp" + + "gxqr23s79ruapxc4j5uskt4htly2salw4drq979d7rcela9wz02el" + + "hypmdzmzlnxuknpgfyfm86pntt8vvkvffma5qc9n50h4mvqhngadq" + + "y3ngqjcym5a\"" + header.Set("WWW-Authenticate", lsatAuthScheme+" "+str) + header.Add("WWW-Authenticate", l402AuthScheme+" "+str) + return header, nil } diff --git a/auth/mock_test.go b/auth/mock_test.go index c2f92b1..29827d8 100644 --- a/auth/mock_test.go +++ b/auth/mock_test.go @@ -5,7 +5,7 @@ import ( "time" "github.com/lightninglabs/aperture/auth" - "github.com/lightninglabs/aperture/lsat" + "github.com/lightninglabs/aperture/l402" "github.com/lightninglabs/aperture/mint" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lntypes" @@ -17,13 +17,13 @@ type mockMint struct { var _ auth.Minter = (*mockMint)(nil) -func (m *mockMint) MintLSAT(_ context.Context, - services ...lsat.Service) (*macaroon.Macaroon, string, error) { +func (m *mockMint) MintL402(_ context.Context, + services ...l402.Service) (*macaroon.Macaroon, string, error) { return nil, "", nil } -func (m *mockMint) VerifyLSAT(_ context.Context, p *mint.VerificationParams) error { +func (m *mockMint) VerifyL402(_ context.Context, p *mint.VerificationParams) error { return nil } diff --git a/challenger/lnc.go b/challenger/lnc.go index d30891f..cb8e96e 100644 --- a/challenger/lnc.go +++ b/challenger/lnc.go @@ -10,7 +10,7 @@ import ( ) // LNCChallenger is a challenger that uses LNC to connect to an lnd backend to -// create new LSAT payment challenges. +// create new L402 payment challenges. type LNCChallenger struct { lndChallenger *LndChallenger nodeConn *lnc.NodeConn @@ -61,7 +61,7 @@ func (l *LNCChallenger) Stop() { l.lndChallenger.Stop() } -// NewChallenge creates a new LSAT payment challenge, returning a payment +// NewChallenge creates a new L402 payment challenge, returning a payment // request (invoice) and the corresponding payment hash. // // NOTE: This is part of the mint.Challenger interface. diff --git a/challenger/lnd.go b/challenger/lnd.go index 00bd53e..48cea58 100644 --- a/challenger/lnd.go +++ b/challenger/lnd.go @@ -13,7 +13,7 @@ import ( "github.com/lightningnetwork/lnd/lntypes" ) -// LndChallenger is a challenger that uses an lnd backend to create new LSAT +// LndChallenger is a challenger that uses an lnd backend to create new L402 // payment challenges. type LndChallenger struct { client InvoiceClient @@ -247,7 +247,7 @@ func (l *LndChallenger) Stop() { l.wg.Wait() } -// NewChallenge creates a new LSAT payment challenge, returning a payment +// NewChallenge creates a new L402 payment challenge, returning a payment // request (invoice) and the corresponding payment hash. // // NOTE: This is part of the mint.Challenger interface. diff --git a/lsat/caveat.go b/l402/caveat.go similarity index 93% rename from lsat/caveat.go rename to l402/caveat.go index 80aa6e0..204c72f 100644 --- a/lsat/caveat.go +++ b/l402/caveat.go @@ -1,4 +1,4 @@ -package lsat +package l402 import ( "errors" @@ -20,10 +20,10 @@ var ( "\"condition=value\"") ) -// Caveat is a predicate that can be applied to an LSAT in order to restrict its -// use in some form. Caveats are evaluated during LSAT verification after the -// LSAT's signature is verified. The predicate of each caveat must hold true in -// order to successfully validate an LSAT. +// Caveat is a predicate that can be applied to an L402 in order to restrict its +// use in some form. Caveats are evaluated during L402 verification after the +// L402's signature is verified. The predicate of each caveat must hold true in +// order to successfully validate an L402. type Caveat struct { // Condition serves as a way to identify a caveat and how to satisfy it. Condition string @@ -92,11 +92,11 @@ func HasCaveat(m *macaroon.Macaroon, cond string) (string, bool) { return *value, true } -// VerifyCaveats determines whether every relevant caveat of an LSAT holds true. +// VerifyCaveats determines whether every relevant caveat of an L402 holds true. // A caveat is considered relevant if a satisfier is provided for it, which is // what we'll use as their evaluation. // -// NOTE: The caveats provided should be in the same order as in the LSAT to +// NOTE: The caveats provided should be in the same order as in the L402 to // ensure the correctness of each satisfier's SatisfyPrevious. func VerifyCaveats(caveats []Caveat, satisfiers ...Satisfier) error { // Construct a set of our satisfiers to determine which caveats we know diff --git a/lsat/caveat_test.go b/l402/caveat_test.go similarity index 98% rename from lsat/caveat_test.go rename to l402/caveat_test.go index 818a86c..6a64c26 100644 --- a/lsat/caveat_test.go +++ b/l402/caveat_test.go @@ -1,4 +1,4 @@ -package lsat +package l402 import ( "errors" @@ -79,7 +79,7 @@ func TestHasCaveat(t *testing.T) { t.Fatal("found unexpected caveat with unknown condition") } - // Add two caveats, one in a valid LSAT format and another invalid. + // Add two caveats, one in a valid L402 format and another invalid. // We'll test that we're still able to determine the macaroon contains // the valid caveat even though there is one that is invalid. invalidCaveat := []byte("invalid") diff --git a/lsat/client_interceptor.go b/l402/client_interceptor.go similarity index 88% rename from lsat/client_interceptor.go rename to l402/client_interceptor.go index 436689d..4ff6f0d 100644 --- a/lsat/client_interceptor.go +++ b/l402/client_interceptor.go @@ -1,4 +1,4 @@ -package lsat +package l402 import ( "context" @@ -44,12 +44,12 @@ const ( AuthHeader = "WWW-Authenticate" // DefaultMaxCostSats is the default maximum amount in satoshis that we - // are going to pay for an LSAT automatically. Does not include routing + // are going to pay for an L402 automatically. Does not include routing // fees. DefaultMaxCostSats = 1000 // DefaultMaxRoutingFeeSats is the default maximum routing fee in - // satoshis that we are going to pay to acquire an LSAT token. + // satoshis that we are going to pay to acquire an L402 token. DefaultMaxRoutingFeeSats = 10 // PaymentTimeout is the maximum time we allow a payment to take before @@ -66,7 +66,7 @@ var ( // authHeaderRegex is the regular expression the payment challenge must // match for us to be able to parse the macaroon and invoice. authHeaderRegex = regexp.MustCompile( - "LSAT macaroon=\"(.*?)\", invoice=\"(.*?)\"", + "(LSAT|L402) macaroon=\"(.*?)\", invoice=\"(.*?)\"", ) // errPaymentFailedTerminally is signaled by the payment tracking method @@ -76,7 +76,7 @@ var ( "failure state") ) -// ClientInterceptor is a gRPC client interceptor that can handle LSAT +// ClientInterceptor is a gRPC client interceptor that can handle L402 // authentication challenges with embedded payment requests. It uses a // connection to lnd to automatically pay for an authentication token. type ClientInterceptor struct { @@ -90,7 +90,7 @@ type ClientInterceptor struct { } // NewInterceptor creates a new gRPC client interceptor that uses the provided -// lnd connection to automatically acquire and pay for LSAT tokens, unless the +// lnd connection to automatically acquire and pay for L402 tokens, unless the // indicated store already contains a usable token. func NewInterceptor(lnd *lndclient.LndServices, store Store, rpcCallTimeout time.Duration, maxCost, @@ -140,7 +140,7 @@ func (i *ClientInterceptor) UnaryInterceptor(ctx context.Context, method string, } // Try executing the call now. If anything goes wrong, we only handle - // the LSAT error message that comes in the form of a gRPC status error. + // the L402 error message that comes in the form of a gRPC status error. rpcCtx, cancel := context.WithTimeout(ctx, i.callTimeout) defer cancel() err = invoker(rpcCtx, method, req, reply, cc, iCtx.opts...) @@ -155,7 +155,7 @@ func (i *ClientInterceptor) UnaryInterceptor(ctx context.Context, method string, return err } - // Execute the same request again, now with the LSAT + // Execute the same request again, now with the L402 // token added as an RPC credential. rpcCtx2, cancel2 := context.WithTimeout(ctx, i.callTimeout) defer cancel2() @@ -188,7 +188,7 @@ func (i *ClientInterceptor) StreamInterceptor(ctx context.Context, } // Try establishing the stream now. If anything goes wrong, we only - // handle the LSAT error message that comes in the form of a gRPC status + // handle the L402 error message that comes in the form of a gRPC status // error. The context of a stream will be used for the whole lifetime of // it, so we can't really clamp down on the initial call with a timeout. stream, err := streamer(ctx, desc, cc, method, iCtx.opts...) @@ -203,7 +203,7 @@ func (i *ClientInterceptor) StreamInterceptor(ctx context.Context, return nil, err } - // Execute the same request again, now with the LSAT token added + // Execute the same request again, now with the L402 token added // as an RPC credential. return streamer(ctx, desc, cc, method, iCtx.opts...) } @@ -240,7 +240,7 @@ func (i *ClientInterceptor) newInterceptContext(ctx context.Context, // this call. We also never send a pending payment to the server since // we know it's not valid. case !iCtx.token.isPending(): - if err = i.addLsatCredentials(iCtx); err != nil { + if err = i.addL402Credentials(iCtx); err != nil { log.Errorf("Adding macaroon to request failed: %v", err) return nil, fmt.Errorf("adding macaroon failed: %v", err) @@ -250,8 +250,8 @@ func (i *ClientInterceptor) newInterceptContext(ctx context.Context, // We need a way to extract the response headers sent by the server. // This can only be done through the experimental grpc.Trailer call // option. We execute the request and inspect the error. If it's the - // LSAT specific payment required error, we might execute the same - // method again later with the paid LSAT token. + // L402 specific payment required error, we might execute the same + // method again later with the paid L402 token. iCtx.opts = append(iCtx.opts, grpc.Trailer(iCtx.metadata)) return iCtx, nil } @@ -262,8 +262,8 @@ func (i *ClientInterceptor) handlePayment(iCtx *interceptContext) error { switch { // Resume/track a pending payment if it was interrupted for some reason. case iCtx.token != nil && iCtx.token.isPending(): - log.Infof("Payment of LSAT token is required, resuming/" + - "tracking previous payment from pending LSAT token") + log.Infof("Payment of L402 token is required, resuming/" + + "tracking previous payment from pending L402 token") err := i.trackPayment(iCtx.mainCtx, iCtx.token) // If the payment failed for good, it will never come back to a @@ -277,9 +277,9 @@ func (i *ClientInterceptor) handlePayment(iCtx *interceptContext) error { } // Let's try again by paying for the new token. - log.Infof("Retrying payment of LSAT token invoice") + log.Infof("Retrying payment of L402 token invoice") var err error - iCtx.token, err = i.payLsatToken( + iCtx.token, err = i.payL402Token( iCtx.mainCtx, iCtx.metadata, ) if err != nil { @@ -295,27 +295,27 @@ func (i *ClientInterceptor) handlePayment(iCtx *interceptContext) error { // We don't have a token yet, try to get a new one. case iCtx.token == nil: // We don't have a token yet, get a new one. - log.Infof("Payment of LSAT token is required, paying invoice") + log.Infof("Payment of L402 token is required, paying invoice") var err error - iCtx.token, err = i.payLsatToken(iCtx.mainCtx, iCtx.metadata) + iCtx.token, err = i.payL402Token(iCtx.mainCtx, iCtx.metadata) if err != nil { return err } // We have a token and it's valid, nothing more to do here. default: - log.Debugf("Found valid LSAT token to add to request") + log.Debugf("Found valid L402 token to add to request") } - if err := i.addLsatCredentials(iCtx); err != nil { + if err := i.addL402Credentials(iCtx); err != nil { log.Errorf("Adding macaroon to request failed: %v", err) return fmt.Errorf("adding macaroon failed: %v", err) } return nil } -// addLsatCredentials adds an LSAT token to the given intercept context. -func (i *ClientInterceptor) addLsatCredentials(iCtx *interceptContext) error { +// addL402Credentials adds an L402 token to the given intercept context. +func (i *ClientInterceptor) addL402Credentials(iCtx *interceptContext) error { if iCtx.token == nil { return fmt.Errorf("cannot add nil token to context") } @@ -330,27 +330,34 @@ func (i *ClientInterceptor) addLsatCredentials(iCtx *interceptContext) error { return nil } -// payLsatToken reads the payment challenge from the response metadata and tries -// to pay the invoice encoded in them, returning a paid LSAT token if +// payL402Token reads the payment challenge from the response metadata and tries +// to pay the invoice encoded in them, returning a paid L402 token if // successful. -func (i *ClientInterceptor) payLsatToken(ctx context.Context, md *metadata.MD) ( +func (i *ClientInterceptor) payL402Token(ctx context.Context, md *metadata.MD) ( *Token, error) { // First parse the authentication header that was stored in the // metadata. - authHeader := md.Get(AuthHeader) - if len(authHeader) == 0 { + authHeaders := md.Get(AuthHeader) + if len(authHeaders) == 0 { return nil, fmt.Errorf("auth header not found in response") } - matches := authHeaderRegex.FindStringSubmatch(authHeader[0]) - if len(matches) != 3 { + // Find the first WWW-Authenticate header, matching authHeaderRegex. + var matches []string + for _, authHeader := range authHeaders { + matches = authHeaderRegex.FindStringSubmatch(authHeader) + if len(matches) == 4 { + break + } + } + if len(matches) != 4 { return nil, fmt.Errorf("invalid auth header "+ - "format: %s", authHeader[0]) + "format: %s", authHeaders[0]) } // Decode the base64 macaroon and the invoice so we can store the // information in our store later. - macBase64, invoiceStr := matches[1], matches[2] + macBase64, invoiceStr := matches[2], matches[3] macBytes, err := base64.StdEncoding.DecodeString(macBase64) if err != nil { return nil, fmt.Errorf("base64 decode of macaroon failed: "+ @@ -364,7 +371,7 @@ func (i *ClientInterceptor) payLsatToken(ctx context.Context, md *metadata.MD) ( // Check that the charged amount does not exceed our maximum cost. maxCostMsat := lnwire.NewMSatFromSatoshis(i.maxCost) if invoice.MilliSat != nil && *invoice.MilliSat > maxCostMsat { - return nil, fmt.Errorf("cannot pay for LSAT automatically, "+ + return nil, fmt.Errorf("cannot pay for L402 automatically, "+ "cost of %d msat exceeds configured max cost of %d "+ "msat", *invoice.MilliSat, maxCostMsat) } diff --git a/lsat/client_interceptor_test.go b/l402/client_interceptor_test.go similarity index 84% rename from lsat/client_interceptor_test.go rename to l402/client_interceptor_test.go index 624ed97..5e6fd9e 100644 --- a/lsat/client_interceptor_test.go +++ b/l402/client_interceptor_test.go @@ -1,4 +1,4 @@ -package lsat +package l402 import ( "context" @@ -23,7 +23,7 @@ type interceptTestCase struct { name string initialPreimage *lntypes.Preimage interceptor *ClientInterceptor - resetCb func() + resetCb func(addL402 bool) expectLndCall bool expectSecondLndCall bool sendPaymentCb func(*testing.T, test.PaymentChannelMessage) @@ -73,17 +73,19 @@ var ( testMacHex = hex.EncodeToString(testMacBytes) paidPreimage = lntypes.Preimage{1, 2, 3, 4, 5} backendErr error - backendAuth = "" + backendAuth = []string{} callMD map[string]string numBackendCalls = 0 overallWg sync.WaitGroup backendWg sync.WaitGroup testCases = []interceptTestCase{{ - name: "no auth required happy path", - initialPreimage: nil, - interceptor: interceptor, - resetCb: func() { resetBackend(nil, "") }, + name: "no auth required happy path", + initialPreimage: nil, + interceptor: interceptor, + resetCb: func(addL402 bool) { + resetBackend(nil, []string{}) + }, expectLndCall: false, expectToken: false, expectBackendCalls: 1, @@ -93,10 +95,10 @@ var ( name: "auth required, no token yet", initialPreimage: nil, interceptor: interceptor, - resetCb: func() { + resetCb: func(addL402 bool) { resetBackend( status.New(GRPCErrCode, GRPCErrMessage).Err(), - makeAuthHeader(testMacBytes), + makeAuthHeaders(testMacBytes, addL402), ) }, expectLndCall: true, @@ -107,7 +109,7 @@ var ( // The next call to the "backend" shouldn't return an // error. - resetBackend(nil, "") + resetBackend(nil, []string{}) msg.Done <- lndclient.PaymentResult{ Preimage: paidPreimage, PaidAmt: 123, @@ -124,10 +126,12 @@ var ( expectMacaroonCall1: false, expectMacaroonCall2: true, }, { - name: "auth required, has token", - initialPreimage: &paidPreimage, - interceptor: interceptor, - resetCb: func() { resetBackend(nil, "") }, + name: "auth required, has token", + initialPreimage: &paidPreimage, + interceptor: interceptor, + resetCb: func(addL402 bool) { + resetBackend(nil, []string{}) + }, expectLndCall: false, expectToken: true, expectBackendCalls: 1, @@ -137,10 +141,10 @@ var ( name: "auth required, has pending token", initialPreimage: &zeroPreimage, interceptor: interceptor, - resetCb: func() { + resetCb: func(addL402 bool) { resetBackend( status.New(GRPCErrCode, GRPCErrMessage).Err(), - makeAuthHeader(testMacBytes), + makeAuthHeaders(testMacBytes, addL402), ) }, expectLndCall: true, @@ -154,7 +158,7 @@ var ( // The next call to the "backend" shouldn't return an // error. - resetBackend(nil, "") + resetBackend(nil, []string{}) msg.Updates <- lndclient.PaymentStatus{ State: lnrpc.Payment_SUCCEEDED, Preimage: paidPreimage, @@ -168,10 +172,10 @@ var ( name: "auth required, has pending but expired token", initialPreimage: &zeroPreimage, interceptor: interceptor, - resetCb: func() { + resetCb: func(addL402 bool) { resetBackend( status.New(GRPCErrCode, GRPCErrMessage).Err(), - makeAuthHeader(testMacBytes), + makeAuthHeaders(testMacBytes, addL402), ) }, expectLndCall: true, @@ -183,7 +187,7 @@ var ( // The next call to the "backend" shouldn't return an // error. - resetBackend(nil, "") + resetBackend(nil, []string{}) msg.Done <- lndclient.PaymentResult{ Preimage: paidPreimage, PaidAmt: 123, @@ -195,7 +199,7 @@ var ( // The next call to the "backend" shouldn't return an // error. - resetBackend(nil, "") + resetBackend(nil, []string{}) msg.Updates <- lndclient.PaymentStatus{ State: lnrpc.Payment_FAILED, } @@ -211,15 +215,15 @@ var ( &lnd.LndServices, store, testTimeout, 100, DefaultMaxRoutingFeeSats, false, ), - resetCb: func() { + resetCb: func(addL402 bool) { resetBackend( status.New(GRPCErrCode, GRPCErrMessage).Err(), - makeAuthHeader(testMacBytes), + makeAuthHeaders(testMacBytes, addL402), ) }, expectLndCall: false, expectToken: false, - expectInterceptErr: "cannot pay for LSAT automatically, cost " + + expectInterceptErr: "cannot pay for L402 automatically, cost " + "of 500000 msat exceeds configured max cost of " + "100000 msat", expectBackendCalls: 1, @@ -230,7 +234,7 @@ var ( // resetBackend is used by the test cases to define the behaviour of the // simulated backend and reset its starting conditions. -func resetBackend(expectedErr error, expectedAuth string) { +func resetBackend(expectedErr error, expectedAuth []string) { backendErr = expectedErr backendAuth = expectedAuth callMD = nil @@ -252,9 +256,9 @@ func invoker(opts []grpc.CallOption) error { // Should we simulate an auth header response? trailer, ok := opt.(grpc.TrailerCallOption) - if ok && backendAuth != "" { + if ok && len(backendAuth) != 0 { trailer.TrailerAddr.Set( - AuthHeader, backendAuth, + AuthHeader, backendAuth..., ) } } @@ -262,7 +266,7 @@ func invoker(opts []grpc.CallOption) error { return backendErr } -// TestUnaryInterceptor tests that the interceptor can handle LSAT protocol +// TestUnaryInterceptor tests that the interceptor can handle L402 protocol // responses for unary calls and pay the token. func TestUnaryInterceptor(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), testTimeout) @@ -284,13 +288,16 @@ func TestUnaryInterceptor(t *testing.T) { ctx, "", nil, nil, nil, unaryInvoker, nil, ) } - t.Run(tc.name, func(t *testing.T) { - testInterceptor(t, tc, intercept) + t.Run(tc.name+" with LSAT header only", func(t *testing.T) { + testInterceptor(t, tc, false, intercept) + }) + t.Run(tc.name+" with LSAT+L402 headers", func(t *testing.T) { + testInterceptor(t, tc, true, intercept) }) } } -// TestStreamInterceptor tests that the interceptor can handle LSAT protocol +// TestStreamInterceptor tests that the interceptor can handle L402 protocol // responses in streams and pay the token. func TestStreamInterceptor(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), testTimeout) @@ -314,18 +321,21 @@ func TestStreamInterceptor(t *testing.T) { ) return err } - t.Run(tc.name, func(t *testing.T) { - testInterceptor(t, tc, intercept) + t.Run(tc.name+" with LSAT header only", func(t *testing.T) { + testInterceptor(t, tc, false, intercept) + }) + t.Run(tc.name+" with LSAT+L402 headers", func(t *testing.T) { + testInterceptor(t, tc, true, intercept) }) } } -func testInterceptor(t *testing.T, tc interceptTestCase, +func testInterceptor(t *testing.T, tc interceptTestCase, addL402 bool, intercept func() error) { // Initial condition and simulated backend call. store.token = makeToken(tc.initialPreimage) - tc.resetCb() + tc.resetCb(addL402) numBackendCalls = 0 backendWg.Add(1) overallWg.Add(1) @@ -434,13 +444,19 @@ func serializeMac(mac *macaroon.Macaroon) []byte { return macBytes } -func makeAuthHeader(macBytes []byte) string { +func makeAuthHeaders(macBytes []byte, addL402 bool) []string { // Testnet invoice over 500 sats. invoice := "lntb5u1p0pskpmpp5jzw9xvdast2g5lm5tswq6n64t2epe3f4xav43dyd" + "239qr8h3yllqdqqcqzpgsp5m8sfjqgugthk66q3tr4gsqr5rh740jrq9x4l0" + "kvj5e77nmwqvpnq9qy9qsq72afzu7sfuppzqg3q2pn49hlh66rv7w60h2rua" + "hx857g94s066yzxcjn4yccqc79779sd232v9ewluvu0tmusvht6r99rld8xs" + "k287cpyac79r" - return fmt.Sprintf("LSAT macaroon=\"%s\", invoice=\"%s\"", + str := fmt.Sprintf("macaroon=\"%s\", invoice=\"%s\"", base64.StdEncoding.EncodeToString(macBytes), invoice) + values := []string{"LSAT " + str} + if addL402 { + values = append(values, "L402 "+str) + } + + return values } diff --git a/lsat/context.go b/l402/context.go similarity index 91% rename from lsat/context.go rename to l402/context.go index c3fc53f..9f7841e 100644 --- a/lsat/context.go +++ b/l402/context.go @@ -1,10 +1,10 @@ -package lsat +package l402 import ( "context" ) -// ContextKey is the type that we use to identify LSAT specific values in the +// ContextKey is the type that we use to identify L402 specific values in the // request context. We wrap the string inside a struct because of this comment // in the context API: "The provided key must be comparable and should not be of // type string or any other built-in type to avoid collisions between packages diff --git a/lsat/credential.go b/l402/credential.go similarity index 99% rename from lsat/credential.go rename to l402/credential.go index 8d38c9e..6dc35cb 100644 --- a/lsat/credential.go +++ b/l402/credential.go @@ -1,4 +1,4 @@ -package lsat +package l402 import ( "context" diff --git a/lsat/header.go b/l402/header.go similarity index 71% rename from lsat/header.go rename to l402/header.go index 8c85803..bca5419 100644 --- a/lsat/header.go +++ b/l402/header.go @@ -1,4 +1,4 @@ -package lsat +package l402 import ( "encoding/base64" @@ -14,27 +14,29 @@ import ( const ( // HeaderAuthorization is the HTTP header field name that is used to - // send the LSAT by REST clients. + // send the L402 by REST clients. HeaderAuthorization = "Authorization" // HeaderMacaroonMD is the HTTP header field name that is used to send - // the LSAT by certain REST and gRPC clients. + // the L402 by certain REST and gRPC clients. HeaderMacaroonMD = "Grpc-Metadata-Macaroon" // HeaderMacaroon is the HTTP header field name that is used to send the - // LSAT by our own gRPC clients. + // L402 by our own gRPC clients. HeaderMacaroon = "Macaroon" ) var ( - authRegex = regexp.MustCompile("LSAT (.*?):([a-f0-9]{64})") - authFormat = "LSAT %s:%s" + authRegex = regexp.MustCompile("(LSAT|L402) (.*?):([a-f0-9]{64})") + authFormatLegacy = "LSAT %s:%s" + authFormat = "L402 %s:%s" ) // FromHeader tries to extract authentication information from HTTP headers. -// There are two supported formats that can be sent in three different header +// There are two supported formats that can be sent in four different header // fields: -// 1. Authorization: LSAT : +// 0. Authorization: LSAT : +// 1. Authorization: L402 : // 2. Grpc-Metadata-Macaroon: // 3. Macaroon: // @@ -49,21 +51,24 @@ func FromHeader(header *http.Header) (*macaroon.Macaroon, lntypes.Preimage, erro case header.Get(HeaderAuthorization) != "": // Parse the content of the header field and check that it is in // the correct format. - authHeader = header.Get(HeaderAuthorization) - log.Debugf("Trying to authorize with header value [%s].", - authHeader) - if !authRegex.MatchString(authHeader) { - return nil, lntypes.Preimage{}, fmt.Errorf("invalid "+ - "auth header format: %s", authHeader) + var matches []string + authHeaders := header.Values(HeaderAuthorization) + for _, authHeader := range authHeaders { + log.Debugf("Trying to authorize with header value "+ + "[%s].", authHeader) + matches = authRegex.FindStringSubmatch(authHeader) + if len(matches) != 4 { + continue + } } - matches := authRegex.FindStringSubmatch(authHeader) - if len(matches) != 3 { + + if len(matches) != 4 { return nil, lntypes.Preimage{}, fmt.Errorf("invalid "+ "auth header format: %s", authHeader) } // Decode the content of the two parts of the header value. - macBase64, preimageHex := matches[1], matches[2] + macBase64, preimageHex := matches[2], matches[3] macBytes, err := base64.StdEncoding.DecodeString(macBase64) if err != nil { return nil, lntypes.Preimage{}, fmt.Errorf("base64 "+ @@ -126,7 +131,7 @@ func FromHeader(header *http.Header) (*macaroon.Macaroon, lntypes.Preimage, erro } // SetHeader sets the provided authentication elements as the default/standard -// HTTP header for the LSAT protocol. +// HTTP header for the L402 protocol. func SetHeader(header *http.Header, mac *macaroon.Macaroon, preimage fmt.Stringer) error { @@ -134,10 +139,17 @@ func SetHeader(header *http.Header, mac *macaroon.Macaroon, if err != nil { return err } - value := fmt.Sprintf( - authFormat, base64.StdEncoding.EncodeToString(macBytes), - preimage.String(), - ) - header.Set(HeaderAuthorization, value) + macStr := base64.StdEncoding.EncodeToString(macBytes) + preimageStr := preimage.String() + + // Send "Authorization: LSAT..." header before sending + // "Authorization: L402" header to be compatible with old aperture. + // TODO: remove this after aperture is upgraded everywhere. + legacyValue := fmt.Sprintf(authFormatLegacy, macStr, preimageStr) + header.Set(HeaderAuthorization, legacyValue) + + value := fmt.Sprintf(authFormat, macStr, preimageStr) + header.Add(HeaderAuthorization, value) + return nil } diff --git a/lsat/identifier.go b/l402/identifier.go similarity index 75% rename from lsat/identifier.go rename to l402/identifier.go index 252540e..776f3ae 100644 --- a/lsat/identifier.go +++ b/l402/identifier.go @@ -1,4 +1,4 @@ -package lsat +package l402 import ( "encoding/binary" @@ -11,14 +11,14 @@ import ( ) const ( - // LatestVersion is the latest version used for minting new LSATs. + // LatestVersion is the latest version used for minting new L402s. LatestVersion = 0 - // SecretSize is the size in bytes of a LSAT's secret, also known as + // SecretSize is the size in bytes of a L402's secret, also known as // the root key of the macaroon. SecretSize = 32 - // TokenIDSize is the size in bytes of an LSAT's ID encoded in its + // TokenIDSize is the size in bytes of an L402's ID encoded in its // macaroon identifier. TokenIDSize = 32 ) @@ -29,11 +29,11 @@ var ( byteOrder = binary.BigEndian // ErrUnknownVersion is an error returned when attempting to decode an - // LSAT identifier with an unknown version. - ErrUnknownVersion = errors.New("unknown LSAT version") + // L402 identifier with an unknown version. + ErrUnknownVersion = errors.New("unknown L402 version") ) -// TokenID is the type that stores the token identifier of an LSAT token. +// TokenID is the type that stores the token identifier of an L402 token. type TokenID [TokenIDSize]byte // String returns the hex encoded representation of the token ID as a string. @@ -58,24 +58,24 @@ func MakeIDFromString(newID string) (TokenID, error) { return id, nil } -// Identifier contains the static identifying details of an LSAT. This is -// intended to be used as the identifier of the macaroon within an LSAT. +// Identifier contains the static identifying details of an L402. This is +// intended to be used as the identifier of the macaroon within an L402. type Identifier struct { - // Version is the version of an LSAT. Having a version allows us to + // Version is the version of an L402. Having a version allows us to // introduce new fields to the identifier in a backwards-compatible // manner. Version uint16 - // PaymentHash is the payment hash linked to an LSAT. Verification of - // an LSAT depends on a valid payment, which is enforced by ensuring a + // PaymentHash is the payment hash linked to an L402. Verification of + // an L402 depends on a valid payment, which is enforced by ensuring a // preimage is provided that hashes to our payment hash. PaymentHash lntypes.Hash - // TokenID is the unique identifier of an LSAT. + // TokenID is the unique identifier of an L402. TokenID TokenID } -// EncodeIdentifier encodes an LSAT's identifier according to its version. +// EncodeIdentifier encodes an L402's identifier according to its version. func EncodeIdentifier(w io.Writer, id *Identifier) error { if err := binary.Write(w, byteOrder, id.Version); err != nil { return err @@ -96,7 +96,7 @@ func EncodeIdentifier(w io.Writer, id *Identifier) error { } } -// DecodeIdentifier decodes an LSAT's identifier according to its version. +// DecodeIdentifier decodes an L402's identifier according to its version. func DecodeIdentifier(r io.Reader) (*Identifier, error) { var version uint16 if err := binary.Read(r, byteOrder, &version); err != nil { diff --git a/lsat/identifier_test.go b/l402/identifier_test.go similarity index 99% rename from lsat/identifier_test.go rename to l402/identifier_test.go index abda64c..7fc39b7 100644 --- a/lsat/identifier_test.go +++ b/l402/identifier_test.go @@ -1,4 +1,4 @@ -package lsat +package l402 import ( "bytes" diff --git a/lsat/log.go b/l402/log.go similarity index 94% rename from lsat/log.go rename to l402/log.go index 6e4f671..1eab231 100644 --- a/lsat/log.go +++ b/l402/log.go @@ -1,4 +1,4 @@ -package lsat +package l402 import ( "github.com/btcsuite/btclog" @@ -6,7 +6,7 @@ import ( ) // Subsystem defines the sub system name of this package. -const Subsystem = "LSAT" +const Subsystem = "L402" // log is a logger that is initialized with no output filters. This // means the package will not perform any logging by default until the caller diff --git a/lsat/satisfier.go b/l402/satisfier.go similarity index 94% rename from lsat/satisfier.go rename to l402/satisfier.go index 2dbbe61..a6125be 100644 --- a/lsat/satisfier.go +++ b/l402/satisfier.go @@ -1,4 +1,4 @@ -package lsat +package l402 import ( "fmt" @@ -18,19 +18,19 @@ type Satisfier struct { // condition can be used multiple times as long as they enforce more // permissions than the previous. // - // For example, we have a caveat that only allows us to use an LSAT for + // For example, we have a caveat that only allows us to use an L402 for // 7 more days. We can add another caveat that only allows for 3 more // days of use and lend it to another party. SatisfyPrevious func(previous Caveat, current Caveat) error - // SatisfyFinal satisfies the final caveat of an LSAT. If multiple + // SatisfyFinal satisfies the final caveat of an L402. If multiple // caveats with the same condition exist, this will only be executed // once all previous caveats are also satisfied. SatisfyFinal func(Caveat) error } // NewServicesSatisfier implements a satisfier to determine whether the target -// service is authorized for a given LSAT. +// service is authorized for a given L402. // // TODO(wilmer): Add tier verification? func NewServicesSatisfier(targetService string) Satisfier { @@ -80,7 +80,7 @@ func NewServicesSatisfier(targetService string) Satisfier { } // NewCapabilitiesSatisfier implements a satisfier to determine whether the -// target capability for a service is authorized for a given LSAT. +// target capability for a service is authorized for a given L402. func NewCapabilitiesSatisfier(service string, targetCapability string) Satisfier { @@ -120,7 +120,7 @@ func NewCapabilitiesSatisfier(service string, } } -// NewTimeoutSatisfier checks if an LSAT is expired or not. The Satisfier takes +// NewTimeoutSatisfier checks if an L402 is expired or not. The Satisfier takes // a service name to set as the condition prefix and currentTimestamp to // compare against the expiration(s) in the caveats. The expiration time is // retrieved from the caveat values themselves. The satisfier will also make @@ -174,7 +174,7 @@ func NewTimeoutSatisfier(service string, now func() time.Time) Satisfier { } return fmt.Errorf("not authorized to access " + - "service. LSAT has expired") + "service. L402 has expired") }, } } diff --git a/lsat/satisfier_test.go b/l402/satisfier_test.go similarity index 99% rename from lsat/satisfier_test.go rename to l402/satisfier_test.go index a50b02e..abdc7e2 100644 --- a/lsat/satisfier_test.go +++ b/l402/satisfier_test.go @@ -1,4 +1,4 @@ -package lsat +package l402 import ( "fmt" diff --git a/lsat/server_interceptor.go b/l402/server_interceptor.go similarity index 92% rename from lsat/server_interceptor.go rename to l402/server_interceptor.go index d9dfe92..f2af76b 100644 --- a/lsat/server_interceptor.go +++ b/l402/server_interceptor.go @@ -1,4 +1,4 @@ -package lsat +package l402 import ( "bytes" @@ -11,11 +11,11 @@ import ( ) // ServerInterceptor is a gRPC server interceptor that extracts the token ID -// from the request context if an LSAT is present. +// from the request context if an L402 is present. type ServerInterceptor struct{} // UnaryInterceptor is an unary gRPC server interceptor that inspects incoming -// calls for authentication tokens. If an LSAT authentication token is found in +// calls for authentication tokens. If an L402 authentication token is found in // the request, its token ID is extracted and treated as client ID. The // extracted ID is then attached to the request context in a format that is easy // to extract by request handlers. @@ -53,7 +53,7 @@ func (w *wrappedStream) Context() context.Context { } // StreamInterceptor is an stream gRPC server interceptor that inspects incoming -// streams for authentication tokens. If an LSAT authentication token is found +// streams for authentication tokens. If an L402 authentication token is found // in the initial stream establishment request, its token ID is extracted and // treated as client ID. The extracted ID is then attached to the request // context in a format that is easy to extract by request handlers. @@ -81,7 +81,7 @@ func (i *ServerInterceptor) StreamInterceptor(srv interface{}, return handler(srv, wrappedStream) } -// tokenFromContext tries to extract the LSAT from a context. +// tokenFromContext tries to extract the L402 from a context. func tokenFromContext(ctx context.Context) (*TokenID, error) { md, ok := metadata.FromIncomingContext(ctx) if !ok { @@ -97,7 +97,7 @@ func tokenFromContext(ctx context.Context) (*TokenID, error) { return nil, fmt.Errorf("auth header extraction failed: %v", err) } - // If there is an LSAT, decode and add it to the context. + // If there is an L402, decode and add it to the context. identifier, err := DecodeIdentifier(bytes.NewBuffer(macaroon.Id())) if err != nil { return nil, fmt.Errorf("token ID decoding failed: %v", err) diff --git a/lsat/service.go b/l402/service.go similarity index 89% rename from lsat/service.go rename to l402/service.go index 2484d20..c595e63 100644 --- a/lsat/service.go +++ b/l402/service.go @@ -1,4 +1,4 @@ -package lsat +package l402 import ( "errors" @@ -33,26 +33,26 @@ var ( "\"name:tier\"") ) -// ServiceTier represents the different possible tiers of an LSAT-enabled +// ServiceTier represents the different possible tiers of an L402-enabled // service. type ServiceTier uint8 const ( - // BaseTier is the base tier of an LSAT-enabled service. This tier - // should be used for any new LSATs that are not part of a service tier + // BaseTier is the base tier of an L402-enabled service. This tier + // should be used for any new L402s that are not part of a service tier // upgrade. BaseTier ServiceTier = iota ) -// Service contains the details of an LSAT-enabled service. +// Service contains the details of an L402-enabled service. type Service struct { - // Name is the name of the LSAT-enabled service. + // Name is the name of the L402-enabled service. Name string - // Tier is the tier of the LSAT-enabled service. + // Tier is the tier of the L402-enabled service. Tier ServiceTier - // Price of service LSAT in satoshis. + // Price of service L402 in satoshis. Price int64 } diff --git a/lsat/service_test.go b/l402/service_test.go similarity index 99% rename from lsat/service_test.go rename to l402/service_test.go index 8564f5b..dc568fa 100644 --- a/lsat/service_test.go +++ b/l402/service_test.go @@ -1,4 +1,4 @@ -package lsat +package l402 import ( "errors" diff --git a/lsat/store.go b/l402/store.go similarity index 87% rename from lsat/store.go rename to l402/store.go index 736e19f..c7e3c07 100644 --- a/lsat/store.go +++ b/l402/store.go @@ -1,4 +1,4 @@ -package lsat +package l402 import ( "errors" @@ -15,11 +15,11 @@ var ( // storeFileName is the name of the file where we store the final, // valid, token to. - storeFileName = "lsat.token" + storeFileName = "l402.token" // storeFileNamePending is the name of the file where we store a pending // token until it was successfully paid for. - storeFileNamePending = "lsat.token.pending" + storeFileNamePending = "l402.token.pending" // errNoReplace is the error that is returned if a new token is // being written to a store that already contains a paid token. @@ -27,7 +27,7 @@ var ( "new token. " + manualRetryHint) ) -// Store is an interface that allows users to store and retrieve an LSAT token. +// Store is an interface that allows users to store and retrieve an L402 token. type Store interface { // CurrentToken returns the token that is currently contained in the // store or an error if there is none. @@ -69,6 +69,37 @@ func NewFileStore(storeDir string) (*FileStore, error) { } } + // If token file and pending token file exist under old names and + // do not exist under current names, rename them. + renames := []struct { + oldName, newName string + }{ + { + oldName: "lsat.token", + newName: storeFileName, + }, + { + oldName: "lsat.token.pending", + newName: storeFileNamePending, + }, + } + for _, rename := range renames { + oldPath := filepath.Join(storeDir, rename.oldName) + newPath := filepath.Join(storeDir, rename.newName) + if _, err := os.Stat(newPath); !errors.Is(err, os.ErrNotExist) { + // New file already exists, skipping. + continue + } + if _, err := os.Stat(oldPath); err != nil { + // Failed to stat old file, skipping. + continue + } + if err := os.Rename(oldPath, newPath); err != nil { + return nil, fmt.Errorf("failed to rename %s to %s: %w", + oldPath, newPath, err) + } + } + return &FileStore{ fileName: filepath.Join(storeDir, storeFileName), fileNamePending: filepath.Join(storeDir, storeFileNamePending), diff --git a/lsat/store_test.go b/l402/store_test.go similarity index 61% rename from lsat/store_test.go rename to l402/store_test.go index 0c80a3c..2e95a7f 100644 --- a/lsat/store_test.go +++ b/l402/store_test.go @@ -1,4 +1,4 @@ -package lsat +package l402 import ( "os" @@ -6,13 +6,14 @@ import ( "testing" "github.com/lightningnetwork/lnd/lntypes" + "github.com/stretchr/testify/require" ) -// TestStore tests the basic functionality of the file based store. +// TestFileStore tests the basic functionality of the file based store. func TestFileStore(t *testing.T) { t.Parallel() - tempDirName, err := os.MkdirTemp("", "lsatstore") + tempDirName, err := os.MkdirTemp("", "l402store") if err != nil { t.Fatal(err) } @@ -128,3 +129,71 @@ func TestFileStore(t *testing.T) { errNoReplace) } } + +// TestFileStorePendingMigration tests migration from lsat.token.pending +// to l402.token.pending. +func TestFileStorePendingMigration(t *testing.T) { + t.Parallel() + + tempDirName := t.TempDir() + + pendingToken := &Token{ + Preimage: zeroPreimage, + baseMac: makeMac(), + } + + store, err := NewFileStore(tempDirName) + require.NoError(t, err) + + // Write a pending token. + require.NoError(t, store.StoreToken(pendingToken)) + + // Rename file on disk to lsat.token.pending to emulate + // a pre-migration state. + newPath := filepath.Join(tempDirName, storeFileNamePending) + oldPath := filepath.Join(tempDirName, "lsat.token.pending") + require.NoError(t, os.Rename(newPath, oldPath)) + + // Open the same directory again. + store1, err := NewFileStore(tempDirName) + require.NoError(t, err) + + // Read the token and compare its value. + token, err := store1.CurrentToken() + require.NoError(t, err) + require.Equal(t, pendingToken.baseMac, token.baseMac) +} + +// TestFileStoreMigration tests migration from lsat.token to l402.token. +func TestFileStoreMigration(t *testing.T) { + t.Parallel() + + tempDirName := t.TempDir() + + paidPreimage := lntypes.Preimage{1, 2, 3, 4, 5} + paidToken := &Token{ + Preimage: paidPreimage, + baseMac: makeMac(), + } + + store, err := NewFileStore(tempDirName) + require.NoError(t, err) + + // Write a token. + require.NoError(t, store.StoreToken(paidToken)) + + // Rename file on disk to lsat.token.pending to emulate + // a pre-migration state. + newPath := filepath.Join(tempDirName, storeFileName) + oldPath := filepath.Join(tempDirName, "lsat.token") + require.NoError(t, os.Rename(newPath, oldPath)) + + // Open the same directory again. + store1, err := NewFileStore(tempDirName) + require.NoError(t, err) + + // Read the token and compare its value. + token, err := store1.CurrentToken() + require.NoError(t, err) + require.Equal(t, paidToken.baseMac, token.baseMac) +} diff --git a/lsat/token.go b/l402/token.go similarity index 94% rename from lsat/token.go rename to l402/token.go index 1be010e..ebd1bd6 100644 --- a/lsat/token.go +++ b/l402/token.go @@ -1,4 +1,4 @@ -package lsat +package l402 import ( "bytes" @@ -17,9 +17,9 @@ var ( zeroPreimage lntypes.Preimage ) -// Token is the main type to store an LSAT token in. +// Token is the main type to store an L402 token in. type Token struct { - // PaymentHash is the hash of the LSAT invoice that needs to be paid. + // PaymentHash is the hash of the L402 invoice that needs to be paid. // Knowing the preimage to this hash is seen as proof of payment by the // authentication server. PaymentHash lntypes.Hash @@ -47,7 +47,7 @@ type Token struct { } // tokenFromChallenge parses the parts that are present in the challenge part -// of the LSAT auth protocol which is the macaroon and the payment hash. +// of the L402 auth protocol which is the macaroon and the payment hash. func tokenFromChallenge(baseMac []byte, paymentHash *[32]byte) (*Token, error) { // First, validate that the macaroon is valid and can be unmarshaled. mac := &macaroon.Macaroon{} @@ -92,11 +92,11 @@ func (t *Token) PaidMacaroon() (*macaroon.Macaroon, error) { // yet expired. func (t *Token) IsValid() bool { // TODO(guggero): Extract and validate from caveat once we add an - // expiration date to the LSAT. + // expiration date to the L402. return true } -// isPending returns true if the payment for the LSAT is still in flight and we +// isPending returns true if the payment for the L402 is still in flight and we // haven't received the preimage yet. func (t *Token) isPending() bool { return t.Preimage == zeroPreimage diff --git a/log.go b/log.go index 8d375c3..62534ce 100644 --- a/log.go +++ b/log.go @@ -3,7 +3,7 @@ package aperture import ( "github.com/btcsuite/btclog" "github.com/lightninglabs/aperture/auth" - "github.com/lightninglabs/aperture/lsat" + "github.com/lightninglabs/aperture/l402" "github.com/lightninglabs/aperture/proxy" "github.com/lightninglabs/lndclient" "github.com/lightningnetwork/lnd" @@ -27,7 +27,7 @@ func SetupLoggers(root *build.RotatingLogWriter, intercept signal.Interceptor) { lnd.SetSubLogger(root, Subsystem, log) lnd.AddSubLogger(root, auth.Subsystem, intercept, auth.UseLogger) - lnd.AddSubLogger(root, lsat.Subsystem, intercept, lsat.UseLogger) + lnd.AddSubLogger(root, l402.Subsystem, intercept, l402.UseLogger) lnd.AddSubLogger(root, proxy.Subsystem, intercept, proxy.UseLogger) lnd.AddSubLogger(root, "LNDC", intercept, lndclient.UseLogger) } diff --git a/mint/mint.go b/mint/mint.go index 41bad66..301c135 100644 --- a/mint/mint.go +++ b/mint/mint.go @@ -9,7 +9,7 @@ import ( "fmt" "time" - "github.com/lightninglabs/aperture/lsat" + "github.com/lightninglabs/aperture/l402" "github.com/lightningnetwork/lnd/lntypes" "gopkg.in/macaroon.v2" ) @@ -20,8 +20,8 @@ var ( ErrSecretNotFound = errors.New("secret not found") ) -// Challenger is an interface used to present requesters of LSATs with a -// challenge that must be satisfied before an LSAT can be validated. This +// Challenger is an interface used to present requesters of L402s with a +// challenge that must be satisfied before an L402 can be validated. This // challenge takes the form of a Lightning payment request. type Challenger interface { // NewChallenge returns a new challenge in the form of a Lightning @@ -34,18 +34,18 @@ type Challenger interface { Stop() } -// SecretStore is the store responsible for storing LSAT secrets. These secrets -// are required for proper verification of each minted LSAT. +// SecretStore is the store responsible for storing L402 secrets. These secrets +// are required for proper verification of each minted L402. type SecretStore interface { // NewSecret creates a new cryptographically random secret which is // keyed by the given hash. - NewSecret(context.Context, [sha256.Size]byte) ([lsat.SecretSize]byte, + NewSecret(context.Context, [sha256.Size]byte) ([l402.SecretSize]byte, error) // GetSecret returns the cryptographically random secret that // corresponds to the given hash. If there is no secret, then // ErrSecretNotFound is returned. - GetSecret(context.Context, [sha256.Size]byte) ([lsat.SecretSize]byte, + GetSecret(context.Context, [sha256.Size]byte) ([l402.SecretSize]byte, error) // RevokeSecret removes the cryptographically random secret that @@ -55,38 +55,38 @@ type SecretStore interface { } // ServiceLimiter abstracts the source of caveats that should be applied to an -// LSAT for a particular service. +// L402 for a particular service. type ServiceLimiter interface { // ServiceCapabilities returns the capabilities caveats for each // service. This determines which capabilities of each service can be // accessed. - ServiceCapabilities(context.Context, ...lsat.Service) ([]lsat.Caveat, + ServiceCapabilities(context.Context, ...l402.Service) ([]l402.Caveat, error) // ServiceConstraints returns the constraints for each service. This // enforces additional constraints on a particular service/service // capability. - ServiceConstraints(context.Context, ...lsat.Service) ([]lsat.Caveat, + ServiceConstraints(context.Context, ...l402.Service) ([]l402.Caveat, error) // ServiceTimeouts returns the timeout caveat for each service. This // will determine if and when service access can expire. - ServiceTimeouts(context.Context, ...lsat.Service) ([]lsat.Caveat, + ServiceTimeouts(context.Context, ...l402.Service) ([]l402.Caveat, error) } -// Config packages all of the required dependencies to instantiate a new LSAT +// Config packages all of the required dependencies to instantiate a new L402 // mint. type Config struct { - // Secrets is our source for LSAT secrets which will be used for + // Secrets is our source for L402 secrets which will be used for // verification purposes. Secrets SecretStore // Challenger is our source of new challenges to present requesters of - // an LSAT with. + // an L402 with. Challenger Challenger - // ServiceLimiter provides us with how we should limit a new LSAT based + // ServiceLimiter provides us with how we should limit a new L402 based // on its target services. ServiceLimiter ServiceLimiter @@ -94,27 +94,27 @@ type Config struct { Now func() time.Time } -// Mint is an entity that is able to mint and verify LSATs for a set of +// Mint is an entity that is able to mint and verify L402s for a set of // services. type Mint struct { cfg Config } -// New creates a new LSAT mint backed by its given dependencies. +// New creates a new L402 mint backed by its given dependencies. func New(cfg *Config) *Mint { return &Mint{cfg: *cfg} } -// MintLSAT mints a new LSAT for the target services. -func (m *Mint) MintLSAT(ctx context.Context, - services ...lsat.Service) (*macaroon.Macaroon, string, error) { +// MintL402 mints a new L402 for the target services. +func (m *Mint) MintL402(ctx context.Context, + services ...l402.Service) (*macaroon.Macaroon, string, error) { - // Let the LSAT value as the price of the most expensive of the + // Let the L402 value as the price of the most expensive of the // services. price := maximumPrice(services) // We'll start by retrieving a new challenge in the form of a Lightning - // payment request to present the requester of the LSAT with. + // payment request to present the requester of the L402 with. paymentRequest, paymentHash, err := m.cfg.Challenger.NewChallenge(price) if err != nil { return nil, "", err @@ -122,7 +122,7 @@ func (m *Mint) MintLSAT(ctx context.Context, // TODO(wilmer): remove invoice if any of the operations below fail? - // We can then proceed to mint the LSAT with a unique identifier that is + // We can then proceed to mint the L402 with a unique identifier that is // mapped to a unique secret. id, err := createUniqueIdentifier(paymentHash) if err != nil { @@ -143,8 +143,8 @@ func (m *Mint) MintLSAT(ctx context.Context, } // Include any restrictions that should be immediately applied to the - // LSAT. - var caveats []lsat.Caveat + // L402. + var caveats []l402.Caveat if len(services) > 0 { var err error caveats, err = m.caveatsForServices(ctx, services...) @@ -154,7 +154,7 @@ func (m *Mint) MintLSAT(ctx context.Context, return nil, "", err } } - if err := lsat.AddFirstPartyCaveats(mac, caveats...); err != nil { + if err := l402.AddFirstPartyCaveats(mac, caveats...); err != nil { // Attempt to revoke the secret to save space. _ = m.cfg.Secrets.RevokeSecret(ctx, idHash) return nil, "", err @@ -165,7 +165,7 @@ func (m *Mint) MintLSAT(ctx context.Context, // maximumPrice determines the necessary price to use for a collection // of services. -func maximumPrice(services []lsat.Service) int64 { +func maximumPrice(services []l402.Service) int64 { var max int64 for _, service := range services { @@ -177,7 +177,7 @@ func maximumPrice(services []lsat.Service) int64 { return max } -// createUniqueIdentifier creates a new LSAT identifier bound to a payment hash +// createUniqueIdentifier creates a new L402 identifier bound to a payment hash // and a randomly generated ID. func createUniqueIdentifier(paymentHash lntypes.Hash) ([]byte, error) { tokenID, err := generateTokenID() @@ -185,32 +185,32 @@ func createUniqueIdentifier(paymentHash lntypes.Hash) ([]byte, error) { return nil, err } - id := &lsat.Identifier{ - Version: lsat.LatestVersion, + id := &l402.Identifier{ + Version: l402.LatestVersion, PaymentHash: paymentHash, TokenID: tokenID, } var buf bytes.Buffer - if err := lsat.EncodeIdentifier(&buf, id); err != nil { + if err := l402.EncodeIdentifier(&buf, id); err != nil { return nil, err } return buf.Bytes(), nil } -// generateTokenID generates a new random LSAT ID. -func generateTokenID() ([lsat.TokenIDSize]byte, error) { - var tokenID [lsat.TokenIDSize]byte +// generateTokenID generates a new random L402 ID. +func generateTokenID() ([l402.TokenIDSize]byte, error) { + var tokenID [l402.TokenIDSize]byte _, err := rand.Read(tokenID[:]) return tokenID, err } // caveatsForServices returns all of the caveats that should be applied to an -// LSAT for the target services. +// L402 for the target services. func (m *Mint) caveatsForServices(ctx context.Context, - services ...lsat.Service) ([]lsat.Caveat, error) { + services ...l402.Service) ([]l402.Caveat, error) { - servicesCaveat, err := lsat.NewServicesCaveat(services...) + servicesCaveat, err := l402.NewServicesCaveat(services...) if err != nil { return nil, err } @@ -231,34 +231,34 @@ func (m *Mint) caveatsForServices(ctx context.Context, return nil, err } - caveats := []lsat.Caveat{servicesCaveat} + caveats := []l402.Caveat{servicesCaveat} caveats = append(caveats, capabilities...) caveats = append(caveats, constraints...) caveats = append(caveats, timeouts...) return caveats, nil } -// VerificationParams holds all of the requirements to properly verify an LSAT. +// VerificationParams holds all of the requirements to properly verify an L402. type VerificationParams struct { - // Macaroon is the macaroon as part of the LSAT we'll attempt to verify. + // Macaroon is the macaroon as part of the L402 we'll attempt to verify. Macaroon *macaroon.Macaroon - // Preimage is the preimage that should correspond to the LSAT's payment + // Preimage is the preimage that should correspond to the L402's payment // hash. Preimage lntypes.Preimage - // TargetService is the target service a user of an LSAT is attempting + // TargetService is the target service a user of an L402 is attempting // to access. TargetService string } -// VerifyLSAT attempts to verify an LSAT with the given parameters. -func (m *Mint) VerifyLSAT(ctx context.Context, +// VerifyL402 attempts to verify an L402 with the given parameters. +func (m *Mint) VerifyL402(ctx context.Context, params *VerificationParams) error { // We'll first perform a quick check to determine if a valid preimage // was provided. - id, err := lsat.DecodeIdentifier(bytes.NewReader(params.Macaroon.Id())) + id, err := l402.DecodeIdentifier(bytes.NewReader(params.Macaroon.Id())) if err != nil { return err } @@ -267,7 +267,7 @@ func (m *Mint) VerifyLSAT(ctx context.Context, id.PaymentHash) } - // If there was, then we'll ensure the LSAT was minted by us. + // If there was, then we'll ensure the L402 was minted by us. secret, err := m.cfg.Secrets.GetSecret( ctx, sha256.Sum256(params.Macaroon.Id()), ) @@ -279,21 +279,21 @@ func (m *Mint) VerifyLSAT(ctx context.Context, return err } - // With the LSAT verified, we'll now inspect its caveats to ensure the + // With the L402 verified, we'll now inspect its caveats to ensure the // target service is authorized. - caveats := make([]lsat.Caveat, 0, len(rawCaveats)) + caveats := make([]l402.Caveat, 0, len(rawCaveats)) for _, rawCaveat := range rawCaveats { - // LSATs can contain third-party caveats that we're not aware + // L402s can contain third-party caveats that we're not aware // of, so just skip those. - caveat, err := lsat.DecodeCaveat(rawCaveat) + caveat, err := l402.DecodeCaveat(rawCaveat) if err != nil { continue } caveats = append(caveats, caveat) } - return lsat.VerifyCaveats( + return l402.VerifyCaveats( caveats, - lsat.NewServicesSatisfier(params.TargetService), - lsat.NewTimeoutSatisfier(params.TargetService, m.cfg.Now), + l402.NewServicesSatisfier(params.TargetService), + l402.NewTimeoutSatisfier(params.TargetService, m.cfg.Now), ) } diff --git a/mint/mint_test.go b/mint/mint_test.go index 48ff57f..b499402 100644 --- a/mint/mint_test.go +++ b/mint/mint_test.go @@ -7,21 +7,21 @@ import ( "testing" "time" - "github.com/lightninglabs/aperture/lsat" + "github.com/lightninglabs/aperture/l402" "github.com/stretchr/testify/require" "gopkg.in/macaroon.v2" ) var ( - testService = lsat.Service{ + testService = l402.Service{ Name: "lightning_loop", - Tier: lsat.BaseTier, + Tier: l402.BaseTier, } ) -// TestBasicLSAT ensures that an LSAT can only access the services it's +// TestBasicL402 ensures that an L402 can only access the services it's // authorized to. -func TestBasicLSAT(t *testing.T) { +func TestBasicL402(t *testing.T) { t.Parallel() ctx := context.Background() @@ -32,10 +32,10 @@ func TestBasicLSAT(t *testing.T) { Now: time.Now, }) - // Mint a basic LSAT which is only able to access the given service. - macaroon, _, err := mint.MintLSAT(ctx, testService) + // Mint a basic L402 which is only able to access the given service. + macaroon, _, err := mint.MintL402(ctx, testService) if err != nil { - t.Fatalf("unable to mint LSAT: %v", err) + t.Fatalf("unable to mint L402: %v", err) } params := VerificationParams{ @@ -43,22 +43,22 @@ func TestBasicLSAT(t *testing.T) { Preimage: testPreimage, TargetService: testService.Name, } - if err := mint.VerifyLSAT(ctx, ¶ms); err != nil { - t.Fatalf("unable to verify LSAT: %v", err) + if err := mint.VerifyL402(ctx, ¶ms); err != nil { + t.Fatalf("unable to verify L402: %v", err) } // It should not be able to access an unknown service. unknownParams := params unknownParams.TargetService = "unknown" - err = mint.VerifyLSAT(ctx, &unknownParams) + err = mint.VerifyL402(ctx, &unknownParams) if !strings.Contains(err.Error(), "not authorized") { - t.Fatal("expected LSAT to not be authorized") + t.Fatal("expected L402 to not be authorized") } } -// TestAdminLSAT ensures that an admin LSAT (one without a services caveat) is +// TestAdminL402 ensures that an admin L402 (one without a services caveat) is // authorized to access any service. -func TestAdminLSAT(t *testing.T) { +func TestAdminL402(t *testing.T) { t.Parallel() ctx := context.Background() @@ -69,10 +69,10 @@ func TestAdminLSAT(t *testing.T) { Now: time.Now, }) - // Mint an admin LSAT by not including any services. - macaroon, _, err := mint.MintLSAT(ctx) + // Mint an admin L402 by not including any services. + macaroon, _, err := mint.MintL402(ctx) if err != nil { - t.Fatalf("unable to mint LSAT: %v", err) + t.Fatalf("unable to mint L402: %v", err) } // It should be able to access any service as it doesn't have a services @@ -82,13 +82,13 @@ func TestAdminLSAT(t *testing.T) { Preimage: testPreimage, TargetService: testService.Name, } - if err := mint.VerifyLSAT(ctx, params); err != nil { - t.Fatalf("unable to verify LSAT: %v", err) + if err := mint.VerifyL402(ctx, params); err != nil { + t.Fatalf("unable to verify L402: %v", err) } } -// TestRevokedLSAT ensures that we can no longer verify a revoked LSAT. -func TestRevokedLSAT(t *testing.T) { +// TestRevokedL402 ensures that we can no longer verify a revoked L402. +func TestRevokedL402(t *testing.T) { t.Parallel() ctx := context.Background() @@ -99,33 +99,33 @@ func TestRevokedLSAT(t *testing.T) { Now: time.Now, }) - // Mint an LSAT and verify it. - lsat, _, err := mint.MintLSAT(ctx) + // Mint an L402 and verify it. + l402, _, err := mint.MintL402(ctx) if err != nil { - t.Fatalf("unable to mint LSAT: %v", err) + t.Fatalf("unable to mint L402: %v", err) } params := &VerificationParams{ - Macaroon: lsat, + Macaroon: l402, Preimage: testPreimage, TargetService: testService.Name, } - if err := mint.VerifyLSAT(ctx, params); err != nil { - t.Fatalf("unable to verify LSAT: %v", err) + if err := mint.VerifyL402(ctx, params); err != nil { + t.Fatalf("unable to verify L402: %v", err) } // Proceed to revoke it. We should no longer be able to verify it after. - idHash := sha256.Sum256(lsat.Id()) + idHash := sha256.Sum256(l402.Id()) if err := mint.cfg.Secrets.RevokeSecret(ctx, idHash); err != nil { - t.Fatalf("unable to revoke LSAT: %v", err) + t.Fatalf("unable to revoke L402: %v", err) } - if err := mint.VerifyLSAT(ctx, params); err != ErrSecretNotFound { + if err := mint.VerifyL402(ctx, params); err != ErrSecretNotFound { t.Fatalf("expected ErrSecretNotFound, got %v", err) } } -// TestTamperedLSAT ensures that an LSAT that has been tampered with by +// TestTamperedL402 ensures that an L402 that has been tampered with by // modifying its signature results in its verification failing. -func TestTamperedLSAT(t *testing.T) { +func TestTamperedL402(t *testing.T) { t.Parallel() ctx := context.Background() @@ -136,21 +136,21 @@ func TestTamperedLSAT(t *testing.T) { Now: time.Now, }) - // Mint a new LSAT and verify it is valid. - mac, _, err := mint.MintLSAT(ctx, testService) + // Mint a new L402 and verify it is valid. + mac, _, err := mint.MintL402(ctx, testService) if err != nil { - t.Fatalf("unable to mint LSAT: %v", err) + t.Fatalf("unable to mint L402: %v", err) } params := VerificationParams{ Macaroon: mac, Preimage: testPreimage, TargetService: testService.Name, } - if err := mint.VerifyLSAT(ctx, ¶ms); err != nil { - t.Fatalf("unable to verify LSAT: %v", err) + if err := mint.VerifyL402(ctx, ¶ms); err != nil { + t.Fatalf("unable to verify L402: %v", err) } - // Create a tampered LSAT from the valid one. + // Create a tampered L402 from the valid one. macBytes, err := mac.MarshalBinary() if err != nil { t.Fatalf("unable to serialize macaroon: %v", err) @@ -161,19 +161,19 @@ func TestTamperedLSAT(t *testing.T) { t.Fatalf("unable to deserialize macaroon: %v", err) } - // Attempting to verify the tampered LSAT should fail. + // Attempting to verify the tampered L402 should fail. tamperedParams := params tamperedParams.Macaroon = &tampered - err = mint.VerifyLSAT(ctx, &tamperedParams) + err = mint.VerifyL402(ctx, &tamperedParams) if !strings.Contains(err.Error(), "signature mismatch") { - t.Fatal("expected tampered LSAT to be invalid") + t.Fatal("expected tampered L402 to be invalid") } } -// TestDemotedServicesLSAT ensures that an LSAT which originally was authorized +// TestDemotedServicesL402 ensures that an L402 which originally was authorized // to access a service, but was then demoted to no longer be the case, is no // longer authorized. -func TestDemotedServicesLSAT(t *testing.T) { +func TestDemotedServicesL402(t *testing.T) { t.Parallel() ctx := context.Background() @@ -187,11 +187,11 @@ func TestDemotedServicesLSAT(t *testing.T) { unauthorizedService := testService unauthorizedService.Name = "unauthorized" - // Mint an LSAT that is able to access two services, one of which will + // Mint an L402 that is able to access two services, one of which will // be denied later on. - mac, _, err := mint.MintLSAT(ctx, testService, unauthorizedService) + mac, _, err := mint.MintL402(ctx, testService, unauthorizedService) if err != nil { - t.Fatalf("unable to mint LSAT: %v", err) + t.Fatalf("unable to mint L402: %v", err) } // It should be able to access both services. @@ -200,41 +200,41 @@ func TestDemotedServicesLSAT(t *testing.T) { Preimage: testPreimage, TargetService: testService.Name, } - if err := mint.VerifyLSAT(ctx, &authorizedParams); err != nil { - t.Fatalf("unable to verify LSAT: %v", err) + if err := mint.VerifyL402(ctx, &authorizedParams); err != nil { + t.Fatalf("unable to verify L402: %v", err) } unauthorizedParams := VerificationParams{ Macaroon: mac, Preimage: testPreimage, TargetService: unauthorizedService.Name, } - if err := mint.VerifyLSAT(ctx, &unauthorizedParams); err != nil { - t.Fatalf("unable to verify LSAT: %v", err) + if err := mint.VerifyL402(ctx, &unauthorizedParams); err != nil { + t.Fatalf("unable to verify L402: %v", err) } // Demote the second service by including an additional services caveat // that only includes the first service. - services, err := lsat.NewServicesCaveat(testService) + services, err := l402.NewServicesCaveat(testService) if err != nil { t.Fatalf("unable to create services caveat: %v", err) } - err = lsat.AddFirstPartyCaveats(mac, services) + err = l402.AddFirstPartyCaveats(mac, services) if err != nil { - t.Fatalf("unable to demote LSAT: %v", err) + t.Fatalf("unable to demote L402: %v", err) } // It should now only be able to access the first, but not the second. - if err := mint.VerifyLSAT(ctx, &authorizedParams); err != nil { - t.Fatalf("unable to verify LSAT: %v", err) + if err := mint.VerifyL402(ctx, &authorizedParams); err != nil { + t.Fatalf("unable to verify L402: %v", err) } - err = mint.VerifyLSAT(ctx, &unauthorizedParams) + err = mint.VerifyL402(ctx, &unauthorizedParams) if !strings.Contains(err.Error(), "not authorized") { t.Fatal("expected macaroon to be invalid") } } -// TestExpiredServicesLSAT asserts the behavior of the Timeout caveat. -func TestExpiredServicesLSAT(t *testing.T) { +// TestExpiredServicesL402 asserts the behavior of the Timeout caveat. +func TestExpiredServicesL402(t *testing.T) { t.Parallel() initialTime := int64(1000) @@ -248,8 +248,8 @@ func TestExpiredServicesLSAT(t *testing.T) { Now: mockTime.now, }) - // Mint a new lsat for accessing a test service. - mac, _, err := mint.MintLSAT(ctx, testService) + // Mint a new l402 for accessing a test service. + mac, _, err := mint.MintL402(ctx, testService) require.NoError(t, err) authorizedParams := VerificationParams{ @@ -259,22 +259,22 @@ func TestExpiredServicesLSAT(t *testing.T) { } // It should be able to access the service if no timeout caveat added. - require.NoError(t, mint.VerifyLSAT(ctx, &authorizedParams)) + require.NoError(t, mint.VerifyL402(ctx, &authorizedParams)) // Add a timeout caveat that expires in the future. - timeout := lsat.NewTimeoutCaveat(testService.Name, 1000, mockTime.now) - require.NoError(t, lsat.AddFirstPartyCaveats(mac, timeout)) + timeout := l402.NewTimeoutCaveat(testService.Name, 1000, mockTime.now) + require.NoError(t, l402.AddFirstPartyCaveats(mac, timeout)) - // Make sure that the LSAT is still valid after timeout is added since + // Make sure that the L402 is still valid after timeout is added since // the timeout has not yet been reached. - require.NoError(t, mint.VerifyLSAT(ctx, &authorizedParams)) + require.NoError(t, mint.VerifyL402(ctx, &authorizedParams)) - // Force time to pass such that the LSAT should no longer be valid. + // Force time to pass such that the L402 should no longer be valid. mockTime.setTime(initialTime + 1001) - // Assert that the LSAT is no longer valid due to the timeout being + // Assert that the L402 is no longer valid due to the timeout being // reached. - err = mint.VerifyLSAT(ctx, &authorizedParams) + err = mint.VerifyL402(ctx, &authorizedParams) require.Contains(t, err.Error(), "not authorized") } diff --git a/mint/mock_test.go b/mint/mock_test.go index 00aa726..9a9c1cd 100644 --- a/mint/mock_test.go +++ b/mint/mock_test.go @@ -5,7 +5,7 @@ import ( "crypto/rand" "crypto/sha256" - "github.com/lightninglabs/aperture/lsat" + "github.com/lightninglabs/aperture/l402" "github.com/lightningnetwork/lnd/lntypes" ) @@ -41,15 +41,15 @@ func (d *mockChallenger) NewChallenge(price int64) (string, lntypes.Hash, } type mockSecretStore struct { - secrets map[[sha256.Size]byte][lsat.SecretSize]byte + secrets map[[sha256.Size]byte][l402.SecretSize]byte } var _ SecretStore = (*mockSecretStore)(nil) func (s *mockSecretStore) NewSecret(ctx context.Context, - id [sha256.Size]byte) ([lsat.SecretSize]byte, error) { + id [sha256.Size]byte) ([l402.SecretSize]byte, error) { - var secret [lsat.SecretSize]byte + var secret [l402.SecretSize]byte if _, err := rand.Read(secret[:]); err != nil { return secret, err } @@ -58,7 +58,7 @@ func (s *mockSecretStore) NewSecret(ctx context.Context, } func (s *mockSecretStore) GetSecret(ctx context.Context, - id [sha256.Size]byte) ([lsat.SecretSize]byte, error) { + id [sha256.Size]byte) ([l402.SecretSize]byte, error) { secret, ok := s.secrets[id] if !ok { @@ -76,30 +76,30 @@ func (s *mockSecretStore) RevokeSecret(ctx context.Context, func newMockSecretStore() *mockSecretStore { return &mockSecretStore{ - secrets: make(map[[sha256.Size]byte][lsat.SecretSize]byte), + secrets: make(map[[sha256.Size]byte][l402.SecretSize]byte), } } type mockServiceLimiter struct { - capabilities map[lsat.Service]lsat.Caveat - constraints map[lsat.Service][]lsat.Caveat - timeouts map[lsat.Service]lsat.Caveat + capabilities map[l402.Service]l402.Caveat + constraints map[l402.Service][]l402.Caveat + timeouts map[l402.Service]l402.Caveat } var _ ServiceLimiter = (*mockServiceLimiter)(nil) func newMockServiceLimiter() *mockServiceLimiter { return &mockServiceLimiter{ - capabilities: make(map[lsat.Service]lsat.Caveat), - constraints: make(map[lsat.Service][]lsat.Caveat), - timeouts: make(map[lsat.Service]lsat.Caveat), + capabilities: make(map[l402.Service]l402.Caveat), + constraints: make(map[l402.Service][]l402.Caveat), + timeouts: make(map[l402.Service]l402.Caveat), } } func (l *mockServiceLimiter) ServiceCapabilities(ctx context.Context, - services ...lsat.Service) ([]lsat.Caveat, error) { + services ...l402.Service) ([]l402.Caveat, error) { - res := make([]lsat.Caveat, 0, len(services)) + res := make([]l402.Caveat, 0, len(services)) for _, service := range services { capabilities, ok := l.capabilities[service] if !ok { @@ -111,9 +111,9 @@ func (l *mockServiceLimiter) ServiceCapabilities(ctx context.Context, } func (l *mockServiceLimiter) ServiceConstraints(ctx context.Context, - services ...lsat.Service) ([]lsat.Caveat, error) { + services ...l402.Service) ([]l402.Caveat, error) { - res := make([]lsat.Caveat, 0, len(services)) + res := make([]l402.Caveat, 0, len(services)) for _, service := range services { constraints, ok := l.constraints[service] if !ok { @@ -125,9 +125,9 @@ func (l *mockServiceLimiter) ServiceConstraints(ctx context.Context, } func (l *mockServiceLimiter) ServiceTimeouts(ctx context.Context, - services ...lsat.Service) ([]lsat.Caveat, error) { + services ...l402.Service) ([]l402.Caveat, error) { - res := make([]lsat.Caveat, 0, len(services)) + res := make([]l402.Caveat, 0, len(services)) for _, service := range services { timeouts, ok := l.timeouts[service] if !ok { diff --git a/proxy/proxy.go b/proxy/proxy.go index 7a11982..1d2bf8f 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -12,7 +12,7 @@ import ( "strings" "github.com/lightninglabs/aperture/auth" - "github.com/lightninglabs/aperture/lsat" + "github.com/lightninglabs/aperture/l402" "google.golang.org/grpc/codes" ) @@ -300,12 +300,12 @@ func (p *Proxy) director(req *http.Request) { // Make sure we always forward the authorization in the correct/ // default format so the backend knows what to do with it. - mac, preimage, err := lsat.FromHeader(&req.Header) + mac, preimage, err := l402.FromHeader(&req.Header) if err == nil { // It could be that there is no auth information because // none is needed for this particular request. So we // only continue if no error is set. - err := lsat.SetHeader(&req.Header, mac, preimage) + err := l402.SetHeader(&req.Header, mac, preimage) if err != nil { log.Errorf("could not set header: %v", err) } @@ -398,8 +398,6 @@ func addCorsHeaders(header http.Header) { func (p *Proxy) handlePaymentRequired(w http.ResponseWriter, r *http.Request, serviceName string, servicePrice int64) { - addCorsHeaders(r.Header) - header, err := p.authenticator.FreshChallengeHeader( r, serviceName, servicePrice, ) @@ -412,6 +410,8 @@ func (p *Proxy) handlePaymentRequired(w http.ResponseWriter, r *http.Request, return } + addCorsHeaders(header) + for name, value := range header { w.Header().Set(name, value[0]) for i := 1; i < len(value); i++ { diff --git a/proxy/proxy_test.go b/proxy/proxy_test.go index 1e0e439..dbe326b 100644 --- a/proxy/proxy_test.go +++ b/proxy/proxy_test.go @@ -15,7 +15,7 @@ import ( "time" "github.com/lightninglabs/aperture/auth" - "github.com/lightninglabs/aperture/lsat" + "github.com/lightninglabs/aperture/l402" "github.com/lightninglabs/aperture/proxy" proxytest "github.com/lightninglabs/aperture/proxy/testdata" "github.com/lightningnetwork/lnd/cert" @@ -87,7 +87,7 @@ func (s *helloServer) SayHelloNoAuth(_ context.Context, } // TestProxyHTTP tests that the proxy can forward HTTP requests to a backend -// service and handle LSAT authentication correctly. +// service and handle L402 authentication correctly. func TestProxyHTTP(t *testing.T) { testCases := []*testCase{{ name: "no whitelist", @@ -108,7 +108,7 @@ func TestProxyHTTP(t *testing.T) { } // TestProxyHTTP tests that the proxy can forward HTTP requests to a backend -// service and handle LSAT authentication correctly. +// service and handle L402 authentication correctly. func runHTTPTest(t *testing.T, tc *testCase) { // Create a list of services to proxy between. services := []*proxy.Service{{ @@ -154,7 +154,7 @@ func runHTTPTest(t *testing.T, tc *testCase) { require.Equal(t, "402 Payment Required", resp.Status) authHeader := resp.Header.Get("Www-Authenticate") - require.Contains(t, authHeader, "LSAT") + require.Regexp(t, "(LSAT|L402)", authHeader) _ = resp.Body.Close() // Make sure that if we query an URL that is on the whitelist, we don't @@ -196,7 +196,7 @@ func runHTTPTest(t *testing.T, tc *testCase) { } // TestProxyHTTP tests that the proxy can forward gRPC requests to a backend -// service and handle LSAT authentication correctly. +// service and handle L402 authentication correctly. func TestProxyGRPC(t *testing.T) { testCases := []*testCase{{ name: "no whitelist", @@ -234,7 +234,7 @@ func TestProxyGRPC(t *testing.T) { } // TestProxyHTTP tests that the proxy can forward gRPC requests to a backend -// service and handle LSAT authentication correctly. +// service and handle L402 authentication correctly. func runGRPCTest(t *testing.T, tc *testCase) { // Since gRPC only really works over TLS, we need to generate a // certificate and key pair first. @@ -309,18 +309,18 @@ func runGRPCTest(t *testing.T, tc *testCase) { grpc.Trailer(&captureMetadata), ) require.Error(t, err) - require.True(t, lsat.IsPaymentRequired(err)) + require.True(t, l402.IsPaymentRequired(err)) - // We expect the WWW-Authenticate header field to be set to an LSAT + // We expect the WWW-Authenticate header field to be set to an L402 // auth response. expectedHeaderContent, _ := mockAuth.FreshChallengeHeader(&http.Request{ Header: map[string][]string{}, }, "", 0) capturedHeader := captureMetadata.Get("WWW-Authenticate") - require.Len(t, capturedHeader, 1) + require.Len(t, capturedHeader, 2) require.Equal( - t, expectedHeaderContent.Get("WWW-Authenticate"), - capturedHeader[0], + t, expectedHeaderContent.Values("WWW-Authenticate"), + capturedHeader, ) // Make sure that if we query an URL that is on the whitelist, we don't diff --git a/proxy/service.go b/proxy/service.go index e1a0634..796c1af 100644 --- a/proxy/service.go +++ b/proxy/service.go @@ -34,8 +34,8 @@ const ( // Service generically specifies configuration data for backend services to the // Aperture proxy. type Service struct { - // Name is the name of the LSAT-enabled service. - Name string `long:"name" description:"Name of the LSAT-enabled service"` + // Name is the name of the L402-enabled service. + Name string `long:"name" description:"Name of the L402-enabled service"` // TLSCertPath is the optional path to the service's TLS certificate. TLSCertPath string `long:"tlscertpath" description:"Path to the service's TLS certificate"` @@ -75,7 +75,7 @@ type Service struct { // Timeout is an optional value that indicates in how many seconds the // service's caveat should time out relative to the time of creation. So // if a value of 100 is set, then the timeout will be 100 seconds - // after creation of the LSAT. + // after creation of the L402. Timeout int64 `long:"timeout" description:"An integer value that indicates the number of seconds until the service access expires"` // Capabilities is the list of capabilities authorized for the service @@ -87,9 +87,9 @@ type Service struct { // correspond to the caveat's condition. Constraints map[string]string `long:"constraints" description:"The service constraints to enforce at the base tier"` - // Price is the custom LSAT value in satoshis to be used for the + // Price is the custom L402 value in satoshis to be used for the // service's endpoint. - Price int64 `long:"price" description:"Static LSAT value in satoshis to be used for this service"` + Price int64 `long:"price" description:"Static L402 value in satoshis to be used for this service"` // DynamicPrice holds the config options needed for initialising // the pricer if a gPRC server is to be used for price data. @@ -99,7 +99,7 @@ type Service struct { // are matched against the path of the URL of a request. If the request // URL matches any of those regular expressions, the call is treated as // if Auth was set to "off". This allows certain RPC methods to not - // require an LSAT token. E.g. the path for a gRPC call looks like this: + // require an L402 token. E.g. the path for a gRPC call looks like this: // /package_name.ServiceName/MethodName AuthWhitelistPaths []string `long:"authwhitelistpaths" description:"List of regular expressions for paths that don't require authentication'"` @@ -217,7 +217,7 @@ func prepareServices(services []*Service) error { // satoshi is to be used. switch { case service.Price == 0: - log.Debugf("Using default LSAT price of %v satoshis for "+ + log.Debugf("Using default L402 price of %v satoshis for "+ "service %s.", defaultServicePrice, service.Name) service.Price = defaultServicePrice case service.Price < 0: diff --git a/sample-conf.yaml b/sample-conf.yaml index 3d275c1..0d09660 100644 --- a/sample-conf.yaml +++ b/sample-conf.yaml @@ -139,11 +139,11 @@ services: # but would not have any effect without additional support added. "valid_until": 1682483169 - # a caveat will be added that expires the LSAT after this many seconds, + # a caveat will be added that expires the L402 after this many seconds, # 31557600 = 1 year. timeout: 31557600 - # The LSAT value in satoshis for the service. It is ignored if + # The L402 value in satoshis for the service. It is ignored if # dynamicprice.enabled is set to true. price: 0 diff --git a/secrets.go b/secrets.go index e7eb3b3..15724ef 100644 --- a/secrets.go +++ b/secrets.go @@ -8,18 +8,18 @@ import ( "fmt" "strings" - "github.com/lightninglabs/aperture/lsat" + "github.com/lightninglabs/aperture/l402" "github.com/lightninglabs/aperture/mint" clientv3 "go.etcd.io/etcd/client/v3" ) var ( - // secretsPrefix is the key we'll use to prefix all LSAT identifiers + // secretsPrefix is the key we'll use to prefix all L402 identifiers // with when storing secrets in an etcd cluster. secretsPrefix = "secrets" ) -// idKey returns the full key to store in the database for an LSAT identifier. +// idKey returns the full key to store in the database for an L402 identifier. // The identifier is hex-encoded in order to prevent conflicts with the etcd key // delimeter. // @@ -32,7 +32,7 @@ func idKey(id [sha256.Size]byte) string { ) } -// secretStore is a store of LSAT secrets backed by an etcd cluster. +// secretStore is a store of L402 secrets backed by an etcd cluster. type secretStore struct { *clientv3.Client } @@ -40,7 +40,7 @@ type secretStore struct { // A compile-time constraint to ensure secretStore implements mint.SecretStore. var _ mint.SecretStore = (*secretStore)(nil) -// newSecretStore instantiates a new LSAT secrets store backed by an etcd +// newSecretStore instantiates a new L402 secrets store backed by an etcd // cluster. func newSecretStore(client *clientv3.Client) *secretStore { return &secretStore{Client: client} @@ -49,9 +49,9 @@ func newSecretStore(client *clientv3.Client) *secretStore { // NewSecret creates a new cryptographically random secret which is keyed by the // given hash. func (s *secretStore) NewSecret(ctx context.Context, - id [sha256.Size]byte) ([lsat.SecretSize]byte, error) { + id [sha256.Size]byte) ([l402.SecretSize]byte, error) { - var secret [lsat.SecretSize]byte + var secret [l402.SecretSize]byte if _, err := rand.Read(secret[:]); err != nil { return secret, err } @@ -63,21 +63,21 @@ func (s *secretStore) NewSecret(ctx context.Context, // GetSecret returns the cryptographically random secret that corresponds to the // given hash. If there is no secret, then mint.ErrSecretNotFound is returned. func (s *secretStore) GetSecret(ctx context.Context, - id [sha256.Size]byte) ([lsat.SecretSize]byte, error) { + id [sha256.Size]byte) ([l402.SecretSize]byte, error) { resp, err := s.Get(ctx, idKey(id)) if err != nil { - return [lsat.SecretSize]byte{}, err + return [l402.SecretSize]byte{}, err } if len(resp.Kvs) == 0 { - return [lsat.SecretSize]byte{}, mint.ErrSecretNotFound + return [l402.SecretSize]byte{}, mint.ErrSecretNotFound } - if len(resp.Kvs[0].Value) != lsat.SecretSize { - return [lsat.SecretSize]byte{}, fmt.Errorf("invalid secret "+ + if len(resp.Kvs[0].Value) != l402.SecretSize { + return [l402.SecretSize]byte{}, fmt.Errorf("invalid secret "+ "size %v", len(resp.Kvs[0].Value)) } - var secret [lsat.SecretSize]byte + var secret [l402.SecretSize]byte copy(secret[:], resp.Kvs[0].Value) return secret, nil } diff --git a/secrets_test.go b/secrets_test.go index 3c213a2..967b546 100644 --- a/secrets_test.go +++ b/secrets_test.go @@ -9,7 +9,7 @@ import ( "testing" "time" - "github.com/lightninglabs/aperture/lsat" + "github.com/lightninglabs/aperture/l402" "github.com/lightninglabs/aperture/mint" clientv3 "go.etcd.io/etcd/client/v3" "go.etcd.io/etcd/server/v3/embed" @@ -64,7 +64,7 @@ func etcdSetup(t *testing.T) (*clientv3.Client, func()) { // identifier exists in the store. If it exists, its value is compared against // the expected secret. func assertSecretExists(t *testing.T, store *secretStore, id [sha256.Size]byte, - expSecret *[lsat.SecretSize]byte) { + expSecret *[l402.SecretSize]byte) { t.Helper() diff --git a/services.go b/services.go index 0cbb149..d703388 100644 --- a/services.go +++ b/services.go @@ -4,7 +4,7 @@ import ( "context" "time" - "github.com/lightninglabs/aperture/lsat" + "github.com/lightninglabs/aperture/l402" "github.com/lightninglabs/aperture/mint" "github.com/lightninglabs/aperture/proxy" ) @@ -13,9 +13,9 @@ import ( // // TODO(wilmer): use etcd instead. type staticServiceLimiter struct { - capabilities map[lsat.Service]lsat.Caveat - constraints map[lsat.Service][]lsat.Caveat - timeouts map[lsat.Service]lsat.Caveat + capabilities map[l402.Service]l402.Caveat + constraints map[l402.Service][]l402.Caveat + timeouts map[l402.Service]l402.Caveat } // A compile-time constraint to ensure staticServiceLimiter implements @@ -27,30 +27,30 @@ var _ mint.ServiceLimiter = (*staticServiceLimiter)(nil) func newStaticServiceLimiter( proxyServices []*proxy.Service) *staticServiceLimiter { - capabilities := make(map[lsat.Service]lsat.Caveat) - constraints := make(map[lsat.Service][]lsat.Caveat) - timeouts := make(map[lsat.Service]lsat.Caveat) + capabilities := make(map[l402.Service]l402.Caveat) + constraints := make(map[l402.Service][]l402.Caveat) + timeouts := make(map[l402.Service]l402.Caveat) for _, proxyService := range proxyServices { - s := lsat.Service{ + s := l402.Service{ Name: proxyService.Name, - Tier: lsat.BaseTier, + Tier: l402.BaseTier, Price: proxyService.Price, } if proxyService.Timeout > 0 { - timeouts[s] = lsat.NewTimeoutCaveat( + timeouts[s] = l402.NewTimeoutCaveat( proxyService.Name, proxyService.Timeout, time.Now, ) } - capabilities[s] = lsat.NewCapabilitiesCaveat( + capabilities[s] = l402.NewCapabilitiesCaveat( proxyService.Name, proxyService.Capabilities, ) for cond, value := range proxyService.Constraints { - caveat := lsat.Caveat{Condition: cond, Value: value} + caveat := l402.Caveat{Condition: cond, Value: value} constraints[s] = append(constraints[s], caveat) } } @@ -65,9 +65,9 @@ func newStaticServiceLimiter( // ServiceCapabilities returns the capabilities caveats for each service. This // determines which capabilities of each service can be accessed. func (l *staticServiceLimiter) ServiceCapabilities(ctx context.Context, - services ...lsat.Service) ([]lsat.Caveat, error) { + services ...l402.Service) ([]l402.Caveat, error) { - res := make([]lsat.Caveat, 0, len(services)) + res := make([]l402.Caveat, 0, len(services)) for _, service := range services { capabilities, ok := l.capabilities[service] if !ok { @@ -82,9 +82,9 @@ func (l *staticServiceLimiter) ServiceCapabilities(ctx context.Context, // ServiceConstraints returns the constraints for each service. This enforces // additional constraints on a particular service/service capability. func (l *staticServiceLimiter) ServiceConstraints(ctx context.Context, - services ...lsat.Service) ([]lsat.Caveat, error) { + services ...l402.Service) ([]l402.Caveat, error) { - res := make([]lsat.Caveat, 0, len(services)) + res := make([]l402.Caveat, 0, len(services)) for _, service := range services { constraints, ok := l.constraints[service] if !ok { @@ -99,9 +99,9 @@ func (l *staticServiceLimiter) ServiceConstraints(ctx context.Context, // ServiceTimeouts returns the timeout caveat for each service. This enforces // an expiration time for service access if enabled. func (l *staticServiceLimiter) ServiceTimeouts(ctx context.Context, - services ...lsat.Service) ([]lsat.Caveat, error) { + services ...l402.Service) ([]l402.Caveat, error) { - res := make([]lsat.Caveat, 0, len(services)) + res := make([]l402.Caveat, 0, len(services)) for _, service := range services { timeout, ok := l.timeouts[service] if !ok { diff --git a/static/index.html b/static/index.html index 7314f87..9fe2a71 100644 --- a/static/index.html +++ b/static/index.html @@ -1,6 +1,6 @@ - LSAT proxy demo page + L402 proxy demo page