diff --git a/README.md b/README.md index 6bd4650e3..89bd307a4 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,8 @@ If you’d prefer to compile from source code please follow [these instructions] Lightning Terminal is backwards compatible with `lnd` back to version v0.13.3-beta. | LiT | LND | -| ---------------- | ------------ | +|------------------| ------------ | +| **v0.6.1-alpha** | v0.13.3-beta | | **v0.6.0-alpha** | v0.13.3-beta | | **v0.5.2-alpha** | v0.12.0-beta | | **v0.5.1-alpha** | v0.12.0-beta | @@ -86,8 +87,8 @@ Lightning Terminal is backwards compatible with `lnd` back to version v0.13.3-be ## Daemon Versions packaged with LiT | LiT | LND | Loop | Faraday | Pool | -| ---------------- | ------------ | ----------- | ------------ |---------------| -| **v0.6.0-alpha** | v0.14.1-beta | v0.15.1-beta | v0.2.7-alpha | v0.5.2-alpha | +|------------------| ------------ | ----------- | ------------ |---------------| +| **v0.6.1-alpha** | v0.14.1-beta | v0.15.1-beta | v0.2.7-alpha | v0.5.2-alpha | | **v0.5.3-alpha** | v0.13.3-beta | v0.14.1-beta | v0.2.6-alpha | v0.5.0-alpha | | **v0.5.2-alpha** | v0.13.3-beta | v0.14.1-beta | v0.2.6-alpha | v0.5.0-alpha | | **v0.5.1-alpha** | v0.13.0-beta | v0.14.1-beta | v0.2.6-alpha | v0.5.0-alpha | diff --git a/app/src/components/connect/ConnectPage.tsx b/app/src/components/connect/ConnectPage.tsx index 9a5d46023..bcf05837e 100644 --- a/app/src/components/connect/ConnectPage.tsx +++ b/app/src/components/connect/ConnectPage.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import { Redirect } from 'react-router'; import { observer } from 'mobx-react-lite'; import styled from '@emotion/styled'; import nodeConnectSvg from 'assets/images/lightning-node-connect.svg'; @@ -12,7 +11,7 @@ import SessionList from './SessionList'; const Styled = { Wrapper: styled.section` - padding: 80px 0 0 80px; + padding-top: 80px; `, DisplayLarge: styled.div` font-family: ${props => props.theme.fonts.open.semiBold}; @@ -33,11 +32,7 @@ const Styled = { const ConnectPage: React.FC = () => { const { l } = usePrefixedTranslation('cmps.connect.ConnectPage'); - const { appView, sessionStore } = useStore(); - - if (!appView.showLightningConnect) { - return ; - } + const { sessionStore } = useStore(); const { Wrapper, DisplayLarge, Description, Divider } = Styled; return !sessionStore.hasMultiple ? ( diff --git a/app/src/components/layout/NavMenu.tsx b/app/src/components/layout/NavMenu.tsx index 6c49f39a8..93bebdeb8 100644 --- a/app/src/components/layout/NavMenu.tsx +++ b/app/src/components/layout/NavMenu.tsx @@ -84,13 +84,7 @@ const NavMenu: React.FC = () => { - {appView.showLightningConnect && ( - - )} + ); diff --git a/app/src/store/views/appView.ts b/app/src/store/views/appView.ts index 3fde494ee..c4aafe911 100644 --- a/app/src/store/views/appView.ts +++ b/app/src/store/views/appView.ts @@ -39,13 +39,6 @@ export default class AppView { } } - /** determines if the Lightning Node Connect UI should be visible */ - get showLightningConnect() { - const devOverride = !!localStorage.getItem('i-want-lnc'); - /** the unix timestamp (ms) when Lightning Node Connect should become visible */ - return devOverride || Date.now() > 1638288000000; // Nov 30 2021 11:00am EST - } - /** Change to the Auth page */ gotoAuth() { this.goTo(`${PUBLIC_URL}/`); diff --git a/rpc_proxy.go b/rpc_proxy.go index 9bdc51c1f..a51c88298 100644 --- a/rpc_proxy.go +++ b/rpc_proxy.go @@ -33,6 +33,24 @@ const ( HeaderMacaroon = "Macaroon" ) +// proxyErr is an error type that adds more context to an error occurring in the +// proxy. +type proxyErr struct { + wrapped error + proxyContext string +} + +// Error returns the error message as a string, including the proxy's context. +func (e *proxyErr) Error() string { + return fmt.Sprintf("proxy error with context %s: %v", e.proxyContext, + e.wrapped) +} + +// Unwrap returns the wrapped error. +func (e *proxyErr) Unwrap() error { + return e.wrapped +} + // newRpcProxy creates a new RPC proxy that can take any native gRPC, grpc-web // or REST request and delegate (and convert if necessary) it to the correct // component. @@ -282,7 +300,7 @@ func (p *rpcProxy) director(ctx context.Context, authHeaders := md.Get("authorization") if len(authHeaders) == 1 { macBytes, err := p.basicAuthToMacaroon( - authHeaders[0], requestURI, + authHeaders[0], requestURI, nil, ) if err != nil { return outCtx, nil, err @@ -332,10 +350,17 @@ func (p *rpcProxy) UnaryServerInterceptor( // have proper macaroon support implemented in the UI. We allow // gRPC web requests to have it and "convert" the auth into a // proper macaroon now. - newCtx, err := p.convertBasicAuth(ctx, info.FullMethod) + newCtx, err := p.convertBasicAuth(ctx, info.FullMethod, nil) if err != nil { - return nil, fmt.Errorf("error upgrading basic auth: %v", - err) + // Make sure we handle the case where the super macaroon + // is still empty on startup. + if pErr, ok := err.(*proxyErr); ok && + pErr.proxyContext == "supermacaroon" { + + return nil, fmt.Errorf("super macaroon error: "+ + "%v", pErr) + } + return nil, err } // With the basic auth converted to a macaroon if necessary, @@ -369,9 +394,19 @@ func (p *rpcProxy) StreamServerInterceptor( // have proper macaroon support implemented in the UI. We allow // gRPC web requests to have it and "convert" the auth into a // proper macaroon now. - ctx, err := p.convertBasicAuth(ss.Context(), info.FullMethod) + ctx, err := p.convertBasicAuth( + ss.Context(), info.FullMethod, nil, + ) if err != nil { - return fmt.Errorf("error upgrading basic auth: %v", err) + // Make sure we handle the case where the super macaroon + // is still empty on startup. + if pErr, ok := err.(*proxyErr); ok && + pErr.proxyContext == "supermacaroon" { + + return fmt.Errorf("super macaroon error: "+ + "%v", pErr) + } + return err } // With the basic auth converted to a macaroon if necessary, @@ -390,21 +425,23 @@ func (p *rpcProxy) StreamServerInterceptor( // convertBasicAuth tries to convert the HTTP authorization header into a // macaroon based authentication header. func (p *rpcProxy) convertBasicAuth(ctx context.Context, - requestURI string) (context.Context, error) { + requestURI string, ctxErr error) (context.Context, error) { md, ok := metadata.FromIncomingContext(ctx) if !ok { - return ctx, nil + return ctx, ctxErr } authHeaders := md.Get("authorization") if len(authHeaders) == 0 { // No basic auth provided, we don't add a macaroon and let the // gRPC security interceptor reject the request. - return ctx, nil + return ctx, ctxErr } - macBytes, err := p.basicAuthToMacaroon(authHeaders[0], requestURI) + macBytes, err := p.basicAuthToMacaroon( + authHeaders[0], requestURI, ctxErr, + ) if err != nil || len(macBytes) == 0 { return ctx, err } @@ -416,8 +453,8 @@ func (p *rpcProxy) convertBasicAuth(ctx context.Context, // basicAuthToMacaroon checks that the incoming request context has the expected // and valid basic authentication header then attaches the correct macaroon to // the context so it can be forwarded to the actual gRPC server. -func (p *rpcProxy) basicAuthToMacaroon(basicAuth, requestURI string) ([]byte, - error) { +func (p *rpcProxy) basicAuthToMacaroon(basicAuth, requestURI string, + ctxErr error) ([]byte, error) { // The user specified an authorization header so this is very likely a // gRPC Web call from the UI. But we only attach the macaroon if the @@ -426,10 +463,10 @@ func (p *rpcProxy) basicAuthToMacaroon(basicAuth, requestURI string) ([]byte, // from the lnd backend. authHeaderParts := strings.Split(basicAuth, " ") if len(authHeaderParts) != 2 { - return nil, nil + return nil, ctxErr } if authHeaderParts[1] != p.basicAuth { - return nil, nil + return nil, ctxErr } var ( @@ -473,7 +510,20 @@ func (p *rpcProxy) basicAuthToMacaroon(basicAuth, requestURI string) ([]byte, // If we have a super macaroon, we can use that one directly since it // will contain all permissions we need. case len(p.superMacaroon) > 0: - return hex.DecodeString(p.superMacaroon) + superMacData, err := hex.DecodeString(p.superMacaroon) + + // Make sure we can avoid running into an empty macaroon here if + // something went wrong with the decoding process (if we're + // still starting up). + if err != nil { + return nil, &proxyErr{ + proxyContext: "supermacaroon", + wrapped: fmt.Errorf("couldn't decode "+ + "super macaroon: %v", err), + } + } + + return superMacData, nil // If we have macaroon data directly, just encode them. This could be // for initial requests to lnd while we don't have the super macaroon @@ -489,7 +539,10 @@ func (p *rpcProxy) basicAuthToMacaroon(basicAuth, requestURI string) ([]byte, return readMacaroon(lncfg.CleanAndExpandPath(macPath)) } - return nil, fmt.Errorf("unknown macaroon to use") + return nil, &proxyErr{ + proxyContext: "auth", + wrapped: fmt.Errorf("unknown macaroon to use"), + } } // dialBufConnBackend dials an in-memory connection to an RPC listener and diff --git a/terminal.go b/terminal.go index db87c0576..943696d28 100644 --- a/terminal.go +++ b/terminal.go @@ -706,9 +706,16 @@ func (g *LightningTerminal) ValidateMacaroon(ctx context.Context, "syncing") } - return g.faradayServer.ValidateMacaroon( + err = g.faradayServer.ValidateMacaroon( ctx, requiredPermissions, fullMethod, ) + if err != nil { + return &proxyErr{ + proxyContext: "faraday", + wrapped: fmt.Errorf("invalid macaroon: %v", + err), + } + } case isLoopURI(fullMethod): // In remote mode we just pass through the request, the remote @@ -723,9 +730,16 @@ func (g *LightningTerminal) ValidateMacaroon(ctx context.Context, "syncing") } - return g.loopServer.ValidateMacaroon( + err = g.loopServer.ValidateMacaroon( ctx, requiredPermissions, fullMethod, ) + if err != nil { + return &proxyErr{ + proxyContext: "loop", + wrapped: fmt.Errorf("invalid macaroon: %v", + err), + } + } case isPoolURI(fullMethod): // In remote mode we just pass through the request, the remote @@ -740,14 +754,26 @@ func (g *LightningTerminal) ValidateMacaroon(ctx context.Context, "syncing") } - return g.poolServer.ValidateMacaroon( + err = g.poolServer.ValidateMacaroon( ctx, requiredPermissions, fullMethod, ) + if err != nil { + return &proxyErr{ + proxyContext: "pool", + wrapped: fmt.Errorf("invalid macaroon: %v", + err), + } + } case isLitURI(fullMethod): - _, err := g.rpcProxy.convertBasicAuth(ctx, fullMethod) + wrap := fmt.Errorf("invalid basic auth") + _, err := g.rpcProxy.convertBasicAuth(ctx, fullMethod, wrap) if err != nil { - return err + return &proxyErr{ + proxyContext: "lit", + wrapped: fmt.Errorf("invalid auth: %v", + err), + } } }