mirror of
https://github.com/lightninglabs/lightning-terminal.git
synced 2026-08-13 12:33:36 +02:00
multi: add PermissionsManager
In this commit, a new PermissionsManager is added. It handles all the active permissions that Lit has access to. This moves us away from using global variables for permission lists. This change might seem overkill on its own but hugely simplifies the permission management once we add lnd subserver permissions.
This commit is contained in:
parent
824e94a08e
commit
c2eb98db38
5 changed files with 147 additions and 93 deletions
|
|
@ -834,7 +834,12 @@ func bakeSuperMacaroon(cfg *LitNodeConfig, readOnly bool) (string, error) {
|
|||
lndAdminCtx := macaroonContext(ctxt, lndAdminMacBytes)
|
||||
lndConn := lnrpc.NewLightningClient(rawConn)
|
||||
|
||||
superMacPermissions := terminal.GetAllPermissions(readOnly)
|
||||
permsMgr, err := terminal.NewPermissionsManager()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
superMacPermissions := permsMgr.ActivePermissions(readOnly)
|
||||
nullID := [4]byte{}
|
||||
superMacHex, err := terminal.BakeSuperMacaroon(
|
||||
lndAdminCtx, lndConn, session.NewSuperMacaroonRootKeyID(nullID),
|
||||
|
|
|
|||
42
rpc_proxy.go
42
rpc_proxy.go
|
|
@ -24,7 +24,6 @@ import (
|
|||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/grpc/test/bufconn"
|
||||
"gopkg.in/macaroon-bakery.v2/bakery"
|
||||
"gopkg.in/macaroon.v2"
|
||||
)
|
||||
|
||||
|
|
@ -59,8 +58,7 @@ func (e *proxyErr) Unwrap() error {
|
|||
// component.
|
||||
func newRpcProxy(cfg *Config, validator macaroons.MacaroonValidator,
|
||||
superMacValidator session.SuperMacaroonValidator,
|
||||
permissionMap map[string][]bakery.Op,
|
||||
bufListener *bufconn.Listener) *rpcProxy {
|
||||
permsMgr *PermissionsManager, bufListener *bufconn.Listener) *rpcProxy {
|
||||
|
||||
// The gRPC web calls are protected by HTTP basic auth which is defined
|
||||
// by base64(username:password). Because we only have a password, we
|
||||
|
|
@ -77,7 +75,7 @@ func newRpcProxy(cfg *Config, validator macaroons.MacaroonValidator,
|
|||
p := &rpcProxy{
|
||||
cfg: cfg,
|
||||
basicAuth: basicAuth,
|
||||
permissionMap: permissionMap,
|
||||
permsMgr: permsMgr,
|
||||
macValidator: validator,
|
||||
superMacValidator: superMacValidator,
|
||||
bufListener: bufListener,
|
||||
|
|
@ -146,9 +144,9 @@ func newRpcProxy(cfg *Config, validator macaroons.MacaroonValidator,
|
|||
// +---------------------+
|
||||
//
|
||||
type rpcProxy struct {
|
||||
cfg *Config
|
||||
basicAuth string
|
||||
permissionMap map[string][]bakery.Op
|
||||
cfg *Config
|
||||
basicAuth string
|
||||
permsMgr *PermissionsManager
|
||||
|
||||
macValidator macaroons.MacaroonValidator
|
||||
superMacValidator session.SuperMacaroonValidator
|
||||
|
|
@ -345,17 +343,17 @@ func (p *rpcProxy) makeDirector(allowLitRPC bool) func(ctx context.Context,
|
|||
// handled by the integrated daemons that are hooking into lnd's
|
||||
// gRPC server.
|
||||
switch {
|
||||
case isFaradayURI(requestURI) && p.cfg.faradayRemote:
|
||||
case p.permsMgr.IsFaradayURI(requestURI) && p.cfg.faradayRemote:
|
||||
return outCtx, p.faradayConn, nil
|
||||
|
||||
case isLoopURI(requestURI) && p.cfg.loopRemote:
|
||||
case p.permsMgr.IsLoopURI(requestURI) && p.cfg.loopRemote:
|
||||
return outCtx, p.loopConn, nil
|
||||
|
||||
case isPoolURI(requestURI) && p.cfg.poolRemote:
|
||||
case p.permsMgr.IsPoolURI(requestURI) && p.cfg.poolRemote:
|
||||
return outCtx, p.poolConn, nil
|
||||
|
||||
// Calls to LiT session RPC aren't allowed in some cases.
|
||||
case isLitURI(requestURI) && !allowLitRPC:
|
||||
case p.permsMgr.IsLitURI(requestURI) && !allowLitRPC:
|
||||
return outCtx, nil, status.Errorf(
|
||||
codes.Unimplemented, "unknown service %s",
|
||||
requestURI,
|
||||
|
|
@ -373,7 +371,7 @@ func (p *rpcProxy) UnaryServerInterceptor(ctx context.Context, req interface{},
|
|||
info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{},
|
||||
error) {
|
||||
|
||||
uriPermissions, ok := p.permissionMap[info.FullMethod]
|
||||
uriPermissions, ok := p.permsMgr.URIPermissions(info.FullMethod)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%s: unknown permissions "+
|
||||
"required for method", info.FullMethod)
|
||||
|
|
@ -414,7 +412,7 @@ func (p *rpcProxy) StreamServerInterceptor(srv interface{},
|
|||
ss grpc.ServerStream, info *grpc.StreamServerInfo,
|
||||
handler grpc.StreamHandler) error {
|
||||
|
||||
uriPermissions, ok := p.permissionMap[info.FullMethod]
|
||||
uriPermissions, ok := p.permsMgr.URIPermissions(info.FullMethod)
|
||||
if !ok {
|
||||
return fmt.Errorf("%s: unknown permissions required "+
|
||||
"for method", info.FullMethod)
|
||||
|
|
@ -503,31 +501,31 @@ func (p *rpcProxy) basicAuthToMacaroon(basicAuth, requestURI string,
|
|||
macData []byte
|
||||
)
|
||||
switch {
|
||||
case isLndURI(requestURI):
|
||||
case p.permsMgr.IsLndURI(requestURI):
|
||||
_, _, _, macPath, macData = p.cfg.lndConnectParams()
|
||||
|
||||
case isFaradayURI(requestURI):
|
||||
case p.permsMgr.IsFaradayURI(requestURI):
|
||||
if p.cfg.faradayRemote {
|
||||
macPath = p.cfg.Remote.Faraday.MacaroonPath
|
||||
} else {
|
||||
macPath = p.cfg.Faraday.MacaroonPath
|
||||
}
|
||||
|
||||
case isLoopURI(requestURI):
|
||||
case p.permsMgr.IsLoopURI(requestURI):
|
||||
if p.cfg.loopRemote {
|
||||
macPath = p.cfg.Remote.Loop.MacaroonPath
|
||||
} else {
|
||||
macPath = p.cfg.Loop.MacaroonPath
|
||||
}
|
||||
|
||||
case isPoolURI(requestURI):
|
||||
case p.permsMgr.IsPoolURI(requestURI):
|
||||
if p.cfg.poolRemote {
|
||||
macPath = p.cfg.Remote.Pool.MacaroonPath
|
||||
} else {
|
||||
macPath = p.cfg.Pool.MacaroonPath
|
||||
}
|
||||
|
||||
case isLitURI(requestURI):
|
||||
case p.permsMgr.IsLitURI(requestURI):
|
||||
macPath = p.cfg.MacaroonPath
|
||||
|
||||
default:
|
||||
|
|
@ -580,7 +578,7 @@ func (p *rpcProxy) basicAuthToMacaroon(basicAuth, requestURI string,
|
|||
func (p *rpcProxy) convertSuperMacaroon(ctx context.Context, macHex string,
|
||||
fullMethod string) ([]byte, error) {
|
||||
|
||||
requiredPermissions, ok := p.permissionMap[fullMethod]
|
||||
requiredPermissions, ok := p.permsMgr.URIPermissions(fullMethod)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%s: unknown permissions required for "+
|
||||
"method", fullMethod)
|
||||
|
|
@ -605,17 +603,17 @@ func (p *rpcProxy) convertSuperMacaroon(ctx context.Context, macHex string,
|
|||
// Is this actually a request that goes to a daemon that is running
|
||||
// remotely?
|
||||
switch {
|
||||
case isFaradayURI(fullMethod) && p.cfg.faradayRemote:
|
||||
case p.permsMgr.IsFaradayURI(fullMethod) && p.cfg.faradayRemote:
|
||||
return readMacaroon(lncfg.CleanAndExpandPath(
|
||||
p.cfg.Remote.Faraday.MacaroonPath,
|
||||
))
|
||||
|
||||
case isLoopURI(fullMethod) && p.cfg.loopRemote:
|
||||
case p.permsMgr.IsLoopURI(fullMethod) && p.cfg.loopRemote:
|
||||
return readMacaroon(lncfg.CleanAndExpandPath(
|
||||
p.cfg.Remote.Loop.MacaroonPath,
|
||||
))
|
||||
|
||||
case isPoolURI(fullMethod) && p.cfg.poolRemote:
|
||||
case p.permsMgr.IsPoolURI(fullMethod) && p.cfg.poolRemote:
|
||||
return readMacaroon(lncfg.CleanAndExpandPath(
|
||||
p.cfg.Remote.Pool.MacaroonPath,
|
||||
))
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ type sessionRpcServerConfig struct {
|
|||
superMacBaker func(ctx context.Context, rootKeyID uint64,
|
||||
recipe *session.MacaroonRecipe) (string, error)
|
||||
firstConnectionDeadline time.Duration
|
||||
permMgr *PermissionsManager
|
||||
}
|
||||
|
||||
// newSessionRPCServer creates a new sessionRpcServer using the passed config.
|
||||
|
|
@ -205,7 +206,7 @@ func (s *sessionRpcServer) resumeSession(sess *session.Session) error {
|
|||
mac, err := s.cfg.superMacBaker(
|
||||
context.Background(), sess.MacaroonRootKey,
|
||||
&session.MacaroonRecipe{
|
||||
Permissions: GetAllPermissions(readOnly),
|
||||
Permissions: s.cfg.permMgr.ActivePermissions(readOnly),
|
||||
Caveats: caveats,
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -26,9 +26,9 @@ var (
|
|||
}},
|
||||
}
|
||||
|
||||
// whiteListedMethods is a map of all lnd RPC methods that don't require
|
||||
// any macaroon authentication.
|
||||
whiteListedMethods = map[string][]bakery.Op{
|
||||
// whiteListedLNDMethods is a map of all lnd RPC methods that don't
|
||||
// require any macaroon authentication.
|
||||
whiteListedLNDMethods = map[string][]bakery.Op{
|
||||
"/lnrpc.WalletUnlocker/GenSeed": {},
|
||||
"/lnrpc.WalletUnlocker/InitWallet": {},
|
||||
"/lnrpc.WalletUnlocker/UnlockWallet": {},
|
||||
|
|
@ -41,53 +41,71 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
// getSubserverPermissions returns a merged map of all subserver macaroon
|
||||
// permissions.
|
||||
func getSubserverPermissions() map[string][]bakery.Op {
|
||||
mapSize := len(faraday.RequiredPermissions) +
|
||||
len(loop.RequiredPermissions) + len(pool.RequiredPermissions)
|
||||
result := make(map[string][]bakery.Op, mapSize)
|
||||
for key, value := range faraday.RequiredPermissions {
|
||||
result[key] = value
|
||||
}
|
||||
for key, value := range loop.RequiredPermissions {
|
||||
result[key] = value
|
||||
}
|
||||
for key, value := range pool.RequiredPermissions {
|
||||
result[key] = value
|
||||
}
|
||||
for key, value := range litPermissions {
|
||||
result[key] = value
|
||||
}
|
||||
return result
|
||||
// subServerName is a name used to identify a particular Lit sub-server.
|
||||
type subServerName string
|
||||
|
||||
const (
|
||||
poolPerms subServerName = "pool"
|
||||
loopPerms subServerName = "loop"
|
||||
faradayPerms subServerName = "faraday"
|
||||
litPerms subServerName = "lit"
|
||||
lndPerms subServerName = "lnd"
|
||||
)
|
||||
|
||||
// PermissionsManager manages the permission lists that Lit requires.
|
||||
type PermissionsManager struct {
|
||||
// fixedPerms is constructed once on creation of the PermissionsManager.
|
||||
// It contains all the permissions that will not change throughout the
|
||||
// lifetime of the manager. It maps sub-server name to uri to permission
|
||||
// operations.
|
||||
fixedPerms map[subServerName]map[string][]bakery.Op
|
||||
|
||||
// perms is a map containing all permissions that the manager knows
|
||||
// are available for use.
|
||||
perms map[string][]bakery.Op
|
||||
}
|
||||
|
||||
// getAllMethodPermissions returns a merged map of lnd's and all subservers'
|
||||
// method macaroon permissions.
|
||||
func getAllMethodPermissions() map[string][]bakery.Op {
|
||||
subserverPermissions := getSubserverPermissions()
|
||||
lndPermissions := lnd.MainRPCServerPermissions()
|
||||
mapSize := len(subserverPermissions) + len(lndPermissions) +
|
||||
len(whiteListedMethods)
|
||||
result := make(map[string][]bakery.Op, mapSize)
|
||||
for key, value := range lndPermissions {
|
||||
result[key] = value
|
||||
// NewPermissionsManager constructs a new PermissionsManager instance and
|
||||
// collects any of the fixed permissions.
|
||||
func NewPermissionsManager() (*PermissionsManager, error) {
|
||||
permissions := make(map[subServerName]map[string][]bakery.Op)
|
||||
permissions[faradayPerms] = faraday.RequiredPermissions
|
||||
permissions[loopPerms] = loop.RequiredPermissions
|
||||
permissions[poolPerms] = pool.RequiredPermissions
|
||||
permissions[litPerms] = litPermissions
|
||||
permissions[lndPerms] = lnd.MainRPCServerPermissions()
|
||||
for k, v := range whiteListedLNDMethods {
|
||||
permissions[lndPerms][k] = v
|
||||
}
|
||||
for key, value := range subserverPermissions {
|
||||
result[key] = value
|
||||
|
||||
allPerms := make(map[string][]bakery.Op)
|
||||
for _, perms := range permissions {
|
||||
for k, v := range perms {
|
||||
allPerms[k] = v
|
||||
}
|
||||
}
|
||||
for key, value := range whiteListedMethods {
|
||||
result[key] = value
|
||||
}
|
||||
return result
|
||||
|
||||
return &PermissionsManager{
|
||||
fixedPerms: permissions,
|
||||
perms: allPerms,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetAllPermissions retrieves all the permissions needed to bake a super
|
||||
// macaroon.
|
||||
func GetAllPermissions(readOnly bool) []bakery.Op {
|
||||
// URIPermissions returns a list of permission operations for the given URI if
|
||||
// the uri is known to the manager. The second return parameter will be false
|
||||
// if the URI is unknown to the manager.
|
||||
func (pm *PermissionsManager) URIPermissions(uri string) ([]bakery.Op, bool) {
|
||||
ops, ok := pm.perms[uri]
|
||||
return ops, ok
|
||||
}
|
||||
|
||||
// ActivePermissions returns all the available active permissions that the
|
||||
// manager is aware of. Optionally, readOnly can be set to true if only the
|
||||
// read-only permissions should be returned.
|
||||
func (pm *PermissionsManager) ActivePermissions(readOnly bool) []bakery.Op {
|
||||
// De-dup the permissions and optionally apply the read-only filter.
|
||||
dedupMap := make(map[string]map[string]bool)
|
||||
|
||||
for _, methodPerms := range getAllMethodPermissions() {
|
||||
for _, methodPerms := range pm.perms {
|
||||
for _, methodPerm := range methodPerms {
|
||||
if methodPerm.Action == "" || methodPerm.Entity == "" {
|
||||
continue
|
||||
|
|
@ -119,32 +137,56 @@ func GetAllPermissions(readOnly bool) []bakery.Op {
|
|||
return result
|
||||
}
|
||||
|
||||
// isLndURI returns true if the given URI belongs to an RPC of lnd.
|
||||
func isLndURI(uri string) bool {
|
||||
_, ok := lnd.MainRPCServerPermissions()[uri]
|
||||
// GetLitPerms returns a map of all permissions that the manager is aware of
|
||||
// _except_ for any LND permissions. In other words, this returns permissions
|
||||
// for which the external validator of Lit is responsible.
|
||||
func (pm *PermissionsManager) GetLitPerms() map[string][]bakery.Op {
|
||||
mapSize := len(pm.fixedPerms[litPerms]) +
|
||||
len(pm.fixedPerms[faradayPerms]) +
|
||||
len(pm.fixedPerms[loopPerms]) + len(pm.fixedPerms[poolPerms])
|
||||
|
||||
result := make(map[string][]bakery.Op, mapSize)
|
||||
for key, value := range pm.fixedPerms[faradayPerms] {
|
||||
result[key] = value
|
||||
}
|
||||
for key, value := range pm.fixedPerms[loopPerms] {
|
||||
result[key] = value
|
||||
}
|
||||
for key, value := range pm.fixedPerms[poolPerms] {
|
||||
result[key] = value
|
||||
}
|
||||
for key, value := range pm.fixedPerms[litPerms] {
|
||||
result[key] = value
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// IsLndURI returns true if the given URI belongs to an RPC of lnd.
|
||||
func (pm *PermissionsManager) IsLndURI(uri string) bool {
|
||||
_, lndCall := pm.fixedPerms[lndPerms][uri]
|
||||
return lndCall
|
||||
}
|
||||
|
||||
// IsLoopURI returns true if the given URI belongs to an RPC of loopd.
|
||||
func (pm *PermissionsManager) IsLoopURI(uri string) bool {
|
||||
_, ok := pm.fixedPerms[loopPerms][uri]
|
||||
return ok
|
||||
}
|
||||
|
||||
// isLoopURI returns true if the given URI belongs to an RPC of loopd.
|
||||
func isLoopURI(uri string) bool {
|
||||
_, ok := loop.RequiredPermissions[uri]
|
||||
// IsFaradayURI returns true if the given URI belongs to an RPC of faraday.
|
||||
func (pm *PermissionsManager) IsFaradayURI(uri string) bool {
|
||||
_, ok := pm.fixedPerms[faradayPerms][uri]
|
||||
return ok
|
||||
}
|
||||
|
||||
// isFaradayURI returns true if the given URI belongs to an RPC of faraday.
|
||||
func isFaradayURI(uri string) bool {
|
||||
_, ok := faraday.RequiredPermissions[uri]
|
||||
// IsPoolURI returns true if the given URI belongs to an RPC of poold.
|
||||
func (pm *PermissionsManager) IsPoolURI(uri string) bool {
|
||||
_, ok := pm.fixedPerms[poolPerms][uri]
|
||||
return ok
|
||||
}
|
||||
|
||||
// isPoolURI returns true if the given URI belongs to an RPC of poold.
|
||||
func isPoolURI(uri string) bool {
|
||||
_, ok := pool.RequiredPermissions[uri]
|
||||
return ok
|
||||
}
|
||||
|
||||
// isLitURI returns true if the given URI belongs to an RPC of LiT.
|
||||
func isLitURI(uri string) bool {
|
||||
_, ok := litPermissions[uri]
|
||||
// IsLitURI returns true if the given URI belongs to an RPC of LiT.
|
||||
func (pm *PermissionsManager) IsLitURI(uri string) bool {
|
||||
_, ok := pm.fixedPerms[litPerms][uri]
|
||||
return ok
|
||||
}
|
||||
|
|
|
|||
24
terminal.go
24
terminal.go
|
|
@ -135,6 +135,8 @@ type LightningTerminal struct {
|
|||
|
||||
defaultImplCfg *lnd.ImplementationCfg
|
||||
|
||||
permsMgr *PermissionsManager
|
||||
|
||||
// lndInterceptorChain is a reference to lnd's interceptor chain that
|
||||
// guards all incoming calls. This is only set in integrated mode!
|
||||
lndInterceptorChain *rpcperms.InterceptorChain
|
||||
|
|
@ -193,6 +195,12 @@ func (g *LightningTerminal) Run() error {
|
|||
// Show version at startup.
|
||||
log.Infof("LiT version: %s", Version())
|
||||
|
||||
// Construct a new PermissionsManager.
|
||||
g.permsMgr, err = NewPermissionsManager()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not create permissions manager")
|
||||
}
|
||||
|
||||
// Create the instances of our subservers now so we can hook them up to
|
||||
// lnd once it's fully started.
|
||||
bufRpcListener := bufconn.Listen(100)
|
||||
|
|
@ -200,8 +208,7 @@ func (g *LightningTerminal) Run() error {
|
|||
g.loopServer = loopd.New(g.cfg.Loop, nil)
|
||||
g.poolServer = pool.NewServer(g.cfg.Pool)
|
||||
g.rpcProxy = newRpcProxy(
|
||||
g.cfg, g, g.validateSuperMacaroon, getAllMethodPermissions(),
|
||||
bufRpcListener,
|
||||
g.cfg, g, g.validateSuperMacaroon, g.permsMgr, bufRpcListener,
|
||||
)
|
||||
g.sessionRpcServer, err = newSessionRPCServer(&sessionRpcServerConfig{
|
||||
basicAuth: g.rpcProxy.basicAuth,
|
||||
|
|
@ -233,6 +240,7 @@ func (g *LightningTerminal) Run() error {
|
|||
)
|
||||
},
|
||||
firstConnectionDeadline: g.cfg.FirstLNCConnDeadline,
|
||||
permMgr: g.permsMgr,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not create new session rpc "+
|
||||
|
|
@ -497,7 +505,7 @@ func (g *LightningTerminal) startSubservers() error {
|
|||
ctx, g.basicClient, session.NewSuperMacaroonRootKeyID(
|
||||
[4]byte{},
|
||||
),
|
||||
GetAllPermissions(false), nil,
|
||||
g.permsMgr.ActivePermissions(false), nil,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -704,7 +712,7 @@ func (g *LightningTerminal) ValidateMacaroon(ctx context.Context,
|
|||
// process. Calls that we proxy to a remote host don't need to be
|
||||
// checked as they'll have their own interceptor.
|
||||
switch {
|
||||
case isFaradayURI(fullMethod):
|
||||
case g.permsMgr.IsFaradayURI(fullMethod):
|
||||
// In remote mode we just pass through the request, the remote
|
||||
// daemon will check the macaroon.
|
||||
if g.cfg.faradayRemote {
|
||||
|
|
@ -728,7 +736,7 @@ func (g *LightningTerminal) ValidateMacaroon(ctx context.Context,
|
|||
}
|
||||
}
|
||||
|
||||
case isLoopURI(fullMethod):
|
||||
case g.permsMgr.IsLoopURI(fullMethod):
|
||||
// In remote mode we just pass through the request, the remote
|
||||
// daemon will check the macaroon.
|
||||
if g.cfg.loopRemote {
|
||||
|
|
@ -752,7 +760,7 @@ func (g *LightningTerminal) ValidateMacaroon(ctx context.Context,
|
|||
}
|
||||
}
|
||||
|
||||
case isPoolURI(fullMethod):
|
||||
case g.permsMgr.IsPoolURI(fullMethod):
|
||||
// In remote mode we just pass through the request, the remote
|
||||
// daemon will check the macaroon.
|
||||
if g.cfg.poolRemote {
|
||||
|
|
@ -776,7 +784,7 @@ func (g *LightningTerminal) ValidateMacaroon(ctx context.Context,
|
|||
}
|
||||
}
|
||||
|
||||
case isLitURI(fullMethod):
|
||||
case g.permsMgr.IsLitURI(fullMethod):
|
||||
if !g.macaroonServiceStarted {
|
||||
return fmt.Errorf("the macaroon service has not " +
|
||||
"started yet")
|
||||
|
|
@ -806,7 +814,7 @@ func (g *LightningTerminal) ValidateMacaroon(ctx context.Context,
|
|||
//
|
||||
// NOTE: This is part of the lnd.ExternalValidator interface.
|
||||
func (g *LightningTerminal) Permissions() map[string][]bakery.Op {
|
||||
return getSubserverPermissions()
|
||||
return g.permsMgr.GetLitPerms()
|
||||
}
|
||||
|
||||
// BuildWalletConfig is responsible for creating or unlocking and then
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue