Skip to content

session_rpcserver+litrpc: allow custom session with all read-only perms #457

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions litrpc/lit-sessions.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 10 additions & 2 deletions litrpc/lit-sessions.proto
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,18 @@ message AddSessionRequest {
}

message MacaroonPermission {
// The entity a permission grants access to.
// The entity a permission grants access to. If a entity is set to the
// "uri" keyword then the action entry should be one of the special cases
// described in the comment for action.
string entity = 1;

// The action that is granted.
// The action that is granted. If entity is set to "uri", then action must
// be set to either:
// - a particular URI to which access should be granted.
// - a URI regex, in which case access will be granted to each URI that
// matches the regex.
// - the "***readonly***" keyword. This will result in the access being
// granted to all read-only endpoints.
string action = 2;
}

Expand Down
6 changes: 6 additions & 0 deletions perms/permissions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,10 @@ func TestMatchRegexURI(t *testing.T) {
uris, isRegex = m.MatchRegexURI("/poolrpc.Trader/.*")
require.True(t, isRegex)
require.Empty(t, uris)

// Assert that the read-only permission's keyword is not seen as a valid
// regex.
uris, isRegex = m.MatchRegexURI("***readonly***")
require.False(t, isRegex)
require.Empty(t, uris)
}
70 changes: 54 additions & 16 deletions session_rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ import (
"gopkg.in/macaroon.v2"
)

// readOnlyAction defines the keyword that a permission action should be set to
// when the entity is set to "uri" in order to activate the special case that
// will result in all read-only permissions known to lit to be added to a
// session's macaroon. The purpose of the three '*'s is to make this keyword
// an invalid URI and an invalid regex so that it does not ever clash with the
// other special cases.
const readOnlyAction = "***readonly***"

// sessionRpcServer is the gRPC server for the Session RPC interface.
type sessionRpcServer struct {
litrpc.UnimplementedSessionsServer
Expand Down Expand Up @@ -126,7 +134,21 @@ func (s *sessionRpcServer) AddSession(_ context.Context,
return nil, err
}

var permissions []bakery.Op
// Store the entity-action permission pairs in a map in order to
// de-dup any repeat perms.
permissions := make(map[string]map[string]struct{})

// addPerm is a closure that can be used to add entity-action pairs to
// the permissions map.
addPerm := func(entity, action string) {
_, ok := permissions[entity]
if !ok {
permissions[entity] = make(map[string]struct{})
}

permissions[entity][action] = struct{}{}
}

switch typ {
// For the default session types we use empty caveats and permissions,
// the macaroons are baked correctly when creating the session.
Expand All @@ -144,10 +166,23 @@ func (s *sessionRpcServer) AddSession(_ context.Context,

for _, op := range req.MacaroonCustomPermissions {
if op.Entity != macaroons.PermissionEntityCustomURI {
permissions = append(permissions, bakery.Op{
Entity: op.Entity,
Action: op.Action,
})
addPerm(op.Entity, op.Action)

continue
}

// If the action specified was equal to the
// readOnlyAction keyword, then this is taken to mean
// that the permissions for all read-only URIs should be
// granted.
if op.Action == readOnlyAction {
readPerms := s.cfg.permMgr.ActivePermissions(
true,
)

for _, p := range readPerms {
addPerm(p.Entity, p.Action)
}

continue
}
Expand All @@ -159,12 +194,7 @@ func (s *sessionRpcServer) AddSession(_ context.Context,
// the matching URIs returned from the
// permissions' manager.
for _, uri := range uris {
permissions = append(
permissions, bakery.Op{
Entity: op.Entity,
Action: uri,
},
)
addPerm(op.Entity, uri)
}
continue
}
Expand All @@ -177,10 +207,7 @@ func (s *sessionRpcServer) AddSession(_ context.Context,
"LiT", op.Action)
}

permissions = append(permissions, bakery.Op{
Entity: op.Entity,
Action: op.Action,
})
addPerm(op.Entity, op.Action)
}

// No other types are currently supported.
Expand All @@ -189,9 +216,20 @@ func (s *sessionRpcServer) AddSession(_ context.Context,
"readonly and custom macaroon types supported in LiT")
}

// Collect the de-duped permissions.
var perms []bakery.Op
for entity, actions := range permissions {
for action := range actions {
perms = append(perms, bakery.Op{
Entity: entity,
Action: action,
})
}
}

sess, err := session.NewSession(
req.Label, typ, expiry, req.MailboxServerAddr, req.DevServer,
permissions, nil,
perms, nil,
)
if err != nil {
return nil, fmt.Errorf("error creating new session: %v", err)
Expand Down