diff --git a/litrpc/lit-sessions.pb.go b/litrpc/lit-sessions.pb.go index 1a8e15147..c6cdf77d8 100644 --- a/litrpc/lit-sessions.pb.go +++ b/litrpc/lit-sessions.pb.go @@ -216,9 +216,17 @@ type MacaroonPermission struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // 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. Entity string `protobuf:"bytes,1,opt,name=entity,proto3" json:"entity,omitempty"` - // 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. Action string `protobuf:"bytes,2,opt,name=action,proto3" json:"action,omitempty"` } diff --git a/litrpc/lit-sessions.proto b/litrpc/lit-sessions.proto index abd916f59..08f081b46 100644 --- a/litrpc/lit-sessions.proto +++ b/litrpc/lit-sessions.proto @@ -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; } diff --git a/perms/permissions_test.go b/perms/permissions_test.go index f5428a0f2..701781f81 100644 --- a/perms/permissions_test.go +++ b/perms/permissions_test.go @@ -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) } diff --git a/session_rpcserver.go b/session_rpcserver.go index 0e5a79ae4..2334320d6 100644 --- a/session_rpcserver.go +++ b/session_rpcserver.go @@ -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 @@ -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. @@ -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 } @@ -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 } @@ -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. @@ -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)