Skip to content

Commit

Permalink
multi: support regex URIs
Browse files Browse the repository at this point in the history
With this commit, a user can now specify a regex when specifying custom
permissions for an LNC session. This regex will be used to select
permissions for URIs that match the regex.
  • Loading branch information
ellemouton committed Nov 29, 2022
1 parent d41f796 commit 15cd1bd
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 6 deletions.
6 changes: 5 additions & 1 deletion cmd/litcli/sessions.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,11 @@ var addSessionCommand = cli.Command{
"this flag will only be used if the 'type' " +
"flag is set to 'custom'. This flag can be " +
"specified multiple times if multiple URIs " +
"should be included",
"should be included. Note that a regex can " +
"also be specified which will then result in " +
"all URIs matching the regex to be included. " +
"For example, '/lnrpc\\..*' will result in " +
"all `lnrpc` permissions being included.",
},
},
}
Expand Down
35 changes: 35 additions & 0 deletions perms/permissions.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package perms

import (
"net"
"regexp"
"strings"
"sync"

Expand Down Expand Up @@ -210,6 +211,40 @@ func (pm *Manager) URIPermissions(uri string) ([]bakery.Op, bool) {
return ops, ok
}

// MatchRegexURI first checks that the given URI is in fact a regex. If it is,
// then it is used to match on the perms that the manager has. The return values
// are a list of URIs that match the regex and the boolean represents whether
// the given uri is in fact a regex.
func (pm *Manager) MatchRegexURI(uriRegex string) ([]string, bool) {
pm.permsMu.RLock()
defer pm.permsMu.RUnlock()

// If the given uri string is one of our permissions, then it is not
// a regex.
if _, ok := pm.perms[uriRegex]; ok {
return nil, false
}

// Construct the regex type from the given string.
r, err := regexp.Compile(uriRegex)
if err != nil {
return nil, false
}

// Iterate over the list of permissions and collect all permissions that
// match the given regex.
var matches []string
for uri := range pm.perms {
if !r.MatchString(uri) {
continue
}

matches = append(matches, uri)
}

return matches, true
}

// 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.
Expand Down
81 changes: 81 additions & 0 deletions perms/permissions_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package perms

import (
"testing"

"github.com/stretchr/testify/require"
"gopkg.in/macaroon-bakery.v2/bakery"
)

// TestMatchRegexURI tests the behaviour of the MatchRegexURI method of the
// Manager.
func TestMatchRegexURI(t *testing.T) {
// Construct a new Manager with a predefined list of perms.
m := &Manager{
perms: map[string][]bakery.Op{
"/lnrpc.WalletUnlocker/GenSeed": {},
"/lnrpc.WalletUnlocker/InitWallet": {},
"/lnrpc.Lightning/SendCoins": {{
Entity: "onchain",
Action: "write",
}},
"/litrpc.Sessions/AddSession": {{
Entity: "sessions",
Action: "write",
}},
"/litrpc.Sessions/ListSessions": {{
Entity: "sessions",
Action: "read",
}},
"/litrpc.Sessions/RevokeSession": {{
Entity: "sessions",
Action: "write",
}},
},
}

// Assert that a full URI is not considered a wild card.
uris, isRegex := m.MatchRegexURI("/litrpc.Sessions/RevokeSession")
require.False(t, isRegex)
require.Empty(t, uris)

// Assert that an invalid URI is also caught as such.
uris, isRegex = m.MatchRegexURI("***")
require.False(t, isRegex)
require.Nil(t, uris)

// Assert that the function correctly matches on a valid wild card for
// litrpc URIs.
uris, isRegex = m.MatchRegexURI("/litrpc.Sessions/.*")
require.True(t, isRegex)
require.ElementsMatch(t, uris, []string{
"/litrpc.Sessions/AddSession",
"/litrpc.Sessions/ListSessions",
"/litrpc.Sessions/RevokeSession",
})

// Assert that the function correctly matches on a valid wild card for
// lnd URIs. First we check that we can specify that only the
// "WalletUnlocker" methods should be included.
uris, isRegex = m.MatchRegexURI("/lnrpc.WalletUnlocker/.*")
require.True(t, isRegex)
require.ElementsMatch(t, uris, []string{
"/lnrpc.WalletUnlocker/GenSeed",
"/lnrpc.WalletUnlocker/InitWallet",
})

// Now we check that we can include all the `lnrpc` methods.
uris, isRegex = m.MatchRegexURI("/lnrpc\\..*")
require.True(t, isRegex)
require.ElementsMatch(t, uris, []string{
"/lnrpc.WalletUnlocker/GenSeed",
"/lnrpc.WalletUnlocker/InitWallet",
"/lnrpc.Lightning/SendCoins",
})

// Assert that the function does not return any URIs for a wild card
// URI that does not match on any of its perms.
uris, isRegex = m.MatchRegexURI("/poolrpc.Trader/.*")
require.True(t, isRegex)
require.Empty(t, uris)
}
36 changes: 31 additions & 5 deletions session_rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,12 +143,38 @@ func (s *sessionRpcServer) AddSession(_ context.Context,
}

for _, op := range req.MacaroonCustomPermissions {
if op.Entity == macaroons.PermissionEntityCustomURI {
_, ok := s.cfg.permMgr.URIPermissions(op.Action)
if !ok {
return nil, fmt.Errorf("URI %s is "+
"unknown to LiT", op.Action)
if op.Entity != macaroons.PermissionEntityCustomURI {
permissions = append(permissions, bakery.Op{
Entity: op.Entity,
Action: op.Action,
})

continue
}

// First check if this is a regex URI.
uris, isRegex := s.cfg.permMgr.MatchRegexURI(op.Action)
if isRegex {
// This is a regex URI, and so we add each of
// the matching URIs returned from the
// permissions' manager.
for _, uri := range uris {
permissions = append(
permissions, bakery.Op{
Entity: op.Entity,
Action: uri,
},
)
}
continue
}

// This is not a wild card URI, so just check that the
// permissions' manager is aware of this URI.
_, ok := s.cfg.permMgr.URIPermissions(op.Action)
if !ok {
return nil, fmt.Errorf("URI %s is unknown to "+
"LiT", op.Action)
}

permissions = append(permissions, bakery.Op{
Expand Down

0 comments on commit 15cd1bd

Please sign in to comment.