Skip to content

Commit 77fbaaf

Browse files
committed
multi: support regex URIs
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.
1 parent d41f796 commit 77fbaaf

File tree

4 files changed

+152
-6
lines changed

4 files changed

+152
-6
lines changed

cmd/litcli/sessions.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,11 @@ var addSessionCommand = cli.Command{
7373
"this flag will only be used if the 'type' " +
7474
"flag is set to 'custom'. This flag can be " +
7575
"specified multiple times if multiple URIs " +
76-
"should be included",
76+
"should be included. Note that a regex can " +
77+
"also be specified which will then result in " +
78+
"all URIs matching the regex to be included. " +
79+
"For example, '/lnrpc\\..*' will result in " +
80+
"all `lnrpc` permissions being included.",
7781
},
7882
},
7983
}

perms/permissions.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package perms
22

33
import (
44
"net"
5+
"regexp"
56
"strings"
67
"sync"
78

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

214+
// MatchRegexURI first checks that the given URI is in-fact a regex. If it is,
215+
// then it is used to match on the perms that the manager has. The return values
216+
// are a list of URIs that match the regex and the boolean represents whether
217+
// the given uri is in fact a regex.
218+
func (pm *Manager) MatchRegexURI(uriRegex string) ([]string, bool) {
219+
pm.permsMu.RLock()
220+
defer pm.permsMu.RUnlock()
221+
222+
// If the given uri string is one of our permissions, then it is not
223+
// a regex.
224+
if _, ok := pm.perms[uriRegex]; ok {
225+
return nil, false
226+
}
227+
228+
// First test to see if the uri is in fact a regex.
229+
r, err := regexp.Compile(uriRegex)
230+
if err != nil {
231+
return nil, false
232+
}
233+
234+
// Iterate over the list of permissions and collect all permissions that
235+
// match the given regex.
236+
var matches []string
237+
for uri := range pm.perms {
238+
if !r.MatchString(uri) {
239+
continue
240+
}
241+
242+
matches = append(matches, uri)
243+
}
244+
245+
return matches, true
246+
}
247+
213248
// ActivePermissions returns all the available active permissions that the
214249
// manager is aware of. Optionally, readOnly can be set to true if only the
215250
// read-only permissions should be returned.

perms/permissions_test.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package perms
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
"gopkg.in/macaroon-bakery.v2/bakery"
8+
)
9+
10+
// TestMatchRegexURI tests the behaviour of the MatchRegexURI method of the
11+
// Manager.
12+
func TestMatchRegexURI(t *testing.T) {
13+
// Construct a new Manager with a predefined list of perms.
14+
m := &Manager{
15+
perms: map[string][]bakery.Op{
16+
"/lnrpc.WalletUnlocker/GenSeed": {},
17+
"/lnrpc.WalletUnlocker/InitWallet": {},
18+
"/lnrpc.Lightning/SendCoins": {{
19+
Entity: "onchain",
20+
Action: "write",
21+
}},
22+
"/litrpc.Sessions/AddSession": {{
23+
Entity: "sessions",
24+
Action: "write",
25+
}},
26+
"/litrpc.Sessions/ListSessions": {{
27+
Entity: "sessions",
28+
Action: "read",
29+
}},
30+
"/litrpc.Sessions/RevokeSession": {{
31+
Entity: "sessions",
32+
Action: "write",
33+
}},
34+
},
35+
}
36+
37+
// Assert that a full URI is not considered a wild card.
38+
uris, isRegex := m.MatchRegexURI("/litrpc.Sessions/RevokeSession")
39+
require.False(t, isRegex)
40+
require.Empty(t, uris)
41+
42+
// Assert that an invalid URI is also caught as such.
43+
uris, isRegex = m.MatchRegexURI("***")
44+
require.False(t, isRegex)
45+
require.Nil(t, uris)
46+
47+
// Assert that the function correctly matches on a valid wild card for
48+
// litrpc URIs.
49+
uris, isRegex = m.MatchRegexURI("/litrpc.Sessions/*")
50+
require.True(t, isRegex)
51+
require.ElementsMatch(t, uris, []string{
52+
"/litrpc.Sessions/AddSession",
53+
"/litrpc.Sessions/ListSessions",
54+
"/litrpc.Sessions/RevokeSession",
55+
})
56+
57+
// Assert that the function correctly matches on a valid wild card for
58+
// lnd URIs. First we check that we can specify that only the
59+
// "WalletUnlocker" methods should be included.
60+
uris, isRegex = m.MatchRegexURI("/lnrpc.WalletUnlocker/*")
61+
require.True(t, isRegex)
62+
require.ElementsMatch(t, uris, []string{
63+
"/lnrpc.WalletUnlocker/GenSeed",
64+
"/lnrpc.WalletUnlocker/InitWallet",
65+
})
66+
67+
// Now we check that we can include all the `lnrpc` methods.
68+
uris, isRegex = m.MatchRegexURI("/lnrpc\\..*")
69+
require.True(t, isRegex)
70+
require.ElementsMatch(t, uris, []string{
71+
"/lnrpc.WalletUnlocker/GenSeed",
72+
"/lnrpc.WalletUnlocker/InitWallet",
73+
"/lnrpc.Lightning/SendCoins",
74+
})
75+
76+
// Assert that the function does not return any URIs for a wild card
77+
// URI that does not match on any of its perms.
78+
uris, isRegex = m.MatchRegexURI("/poolrpc.Trader/*")
79+
require.True(t, isRegex)
80+
require.Empty(t, uris)
81+
}

session_rpcserver.go

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -143,12 +143,38 @@ func (s *sessionRpcServer) AddSession(_ context.Context,
143143
}
144144

145145
for _, op := range req.MacaroonCustomPermissions {
146-
if op.Entity == macaroons.PermissionEntityCustomURI {
147-
_, ok := s.cfg.permMgr.URIPermissions(op.Action)
148-
if !ok {
149-
return nil, fmt.Errorf("URI %s is "+
150-
"unknown to LiT", op.Action)
146+
if op.Entity != macaroons.PermissionEntityCustomURI {
147+
permissions = append(permissions, bakery.Op{
148+
Entity: op.Entity,
149+
Action: op.Action,
150+
})
151+
152+
continue
153+
}
154+
155+
// First check if this is a regex URI.
156+
uris, isRegex := s.cfg.permMgr.MatchRegexURI(op.Action)
157+
if isRegex {
158+
// This is a regex URI, and so we add each of
159+
// the matching URIs returned from the
160+
// permissions' manager.
161+
for _, uri := range uris {
162+
permissions = append(
163+
permissions, bakery.Op{
164+
Entity: op.Entity,
165+
Action: uri,
166+
},
167+
)
151168
}
169+
continue
170+
}
171+
172+
// This is not a wild card URI, so just check that the
173+
// permissions' manager is aware of this URI.
174+
_, ok := s.cfg.permMgr.URIPermissions(op.Action)
175+
if !ok {
176+
return nil, fmt.Errorf("URI %s is unknown to "+
177+
"LiT", op.Action)
152178
}
153179

154180
permissions = append(permissions, bakery.Op{

0 commit comments

Comments
 (0)