session+lnrpc: add StateReserved and delete these on start up

This commit is contained in:
Elle Mouton 2025-02-09 12:49:01 +02:00
parent 68ae704229
commit 6717d9d54b
No known key found for this signature in database
GPG key ID: D7D916376026F177
11 changed files with 210 additions and 33 deletions

View file

@ -688,6 +688,7 @@ export interface SessionStateMap {
STATE_IN_USE: 1;
STATE_REVOKED: 2;
STATE_EXPIRED: 3;
STATE_RESERVED: 4;
}
export const SessionState: SessionStateMap;

View file

@ -5276,7 +5276,8 @@ proto.litrpc.SessionState = {
STATE_CREATED: 0,
STATE_IN_USE: 1,
STATE_REVOKED: 2,
STATE_EXPIRED: 3
STATE_EXPIRED: 3,
STATE_RESERVED: 4
};
goog.object.extend(exports, proto.litrpc);

View file

@ -662,7 +662,8 @@
"STATE_CREATED",
"STATE_IN_USE",
"STATE_REVOKED",
"STATE_EXPIRED"
"STATE_EXPIRED",
"STATE_RESERVED"
],
"default": "STATE_CREATED"
},

View file

@ -81,10 +81,11 @@ func (SessionType) EnumDescriptor() ([]byte, []int) {
type SessionState int32
const (
SessionState_STATE_CREATED SessionState = 0
SessionState_STATE_IN_USE SessionState = 1
SessionState_STATE_REVOKED SessionState = 2
SessionState_STATE_EXPIRED SessionState = 3
SessionState_STATE_CREATED SessionState = 0
SessionState_STATE_IN_USE SessionState = 1
SessionState_STATE_REVOKED SessionState = 2
SessionState_STATE_EXPIRED SessionState = 3
SessionState_STATE_RESERVED SessionState = 4
)
// Enum value maps for SessionState.
@ -94,12 +95,14 @@ var (
1: "STATE_IN_USE",
2: "STATE_REVOKED",
3: "STATE_EXPIRED",
4: "STATE_RESERVED",
}
SessionState_value = map[string]int32{
"STATE_CREATED": 0,
"STATE_IN_USE": 1,
"STATE_REVOKED": 2,
"STATE_EXPIRED": 3,
"STATE_CREATED": 0,
"STATE_IN_USE": 1,
"STATE_REVOKED": 2,
"STATE_EXPIRED": 3,
"STATE_RESERVED": 4,
}
)
@ -1897,31 +1900,32 @@ var file_lit_sessions_proto_rawDesc = []byte{
0x12, 0x12, 0x0a, 0x0e, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x55, 0x54, 0x4f, 0x50, 0x49, 0x4c,
0x4f, 0x54, 0x10, 0x04, 0x12, 0x19, 0x0a, 0x15, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x41, 0x43,
0x41, 0x52, 0x4f, 0x4f, 0x4e, 0x5f, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x05, 0x2a,
0x59, 0x0a, 0x0c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12,
0x6d, 0x0a, 0x0c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12,
0x11, 0x0a, 0x0d, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x44,
0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x49, 0x4e, 0x5f, 0x55,
0x53, 0x45, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x52, 0x45,
0x56, 0x4f, 0x4b, 0x45, 0x44, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x54, 0x41, 0x54, 0x45,
0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x45, 0x44, 0x10, 0x03, 0x32, 0xe8, 0x01, 0x0a, 0x08, 0x53,
0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x43, 0x0a, 0x0a, 0x41, 0x64, 0x64, 0x53, 0x65,
0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x2e, 0x6c, 0x69, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41,
0x64, 0x64, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x1a, 0x2e, 0x6c, 0x69, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x53, 0x65, 0x73,
0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x0c,
0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1b, 0x2e, 0x6c,
0x69, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f,
0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x69, 0x74, 0x72,
0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0d, 0x52, 0x65, 0x76, 0x6f, 0x6b,
0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x2e, 0x6c, 0x69, 0x74, 0x72, 0x70,
0x63, 0x2e, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x69, 0x74, 0x72, 0x70, 0x63, 0x2e,
0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6c, 0x61, 0x62,
0x73, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x2d, 0x74, 0x65, 0x72, 0x6d,
0x69, 0x6e, 0x61, 0x6c, 0x2f, 0x6c, 0x69, 0x74, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x45, 0x44, 0x10, 0x03, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x54,
0x41, 0x54, 0x45, 0x5f, 0x52, 0x45, 0x53, 0x45, 0x52, 0x56, 0x45, 0x44, 0x10, 0x04, 0x32, 0xe8,
0x01, 0x0a, 0x08, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x43, 0x0a, 0x0a, 0x41,
0x64, 0x64, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x2e, 0x6c, 0x69, 0x74, 0x72,
0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x69, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64,
0x64, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x49, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73,
0x12, 0x1b, 0x2e, 0x6c, 0x69, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65,
0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e,
0x6c, 0x69, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69,
0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0d, 0x52,
0x65, 0x76, 0x6f, 0x6b, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x2e, 0x6c,
0x69, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x53, 0x65, 0x73, 0x73,
0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x69, 0x74,
0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f,
0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x69, 0x74,
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e,
0x67, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x2d,
0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x2f, 0x6c, 0x69, 0x74, 0x72, 0x70, 0x63, 0x62,
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (

View file

@ -97,6 +97,7 @@ enum SessionState {
STATE_IN_USE = 1;
STATE_REVOKED = 2;
STATE_EXPIRED = 3;
STATE_RESERVED = 4;
}
message AddSessionResponse {

View file

@ -521,7 +521,8 @@
"STATE_CREATED",
"STATE_IN_USE",
"STATE_REVOKED",
"STATE_EXPIRED"
"STATE_EXPIRED",
"STATE_RESERVED"
],
"default": "STATE_CREATED"
},

View file

@ -97,6 +97,7 @@ enum SessionState {
STATE_IN_USE = 1;
STATE_REVOKED = 2;
STATE_EXPIRED = 3;
STATE_RESERVED = 4;
}
message AddSessionResponse {

View file

@ -51,6 +51,12 @@ const (
// StateExpired is the state of a session that has passed its expiry
// date.
StateExpired State = 3
// StateReserved is a temporary initial state of a session. On start-up,
// any sessions in this state should be cleaned up.
//
// NOTE: this isn't used yet.
StateReserved State = 4
)
// MacaroonRecipe defines the permissions and caveats that should be used
@ -215,5 +221,9 @@ type Store interface {
CheckSessionGroupPredicate(groupID ID,
fn func(s *Session) bool) (bool, error)
// DeleteReservedSessions deletes all sessions that are in the
// StateReserved state.
DeleteReservedSessions() error
IDToGroupIndex
}

View file

@ -444,6 +444,99 @@ func (db *BoltStore) listSessions(filterFn func(s *Session) bool) ([]*Session,
return sessions, nil
}
// DeleteReservedSessions deletes all sessions that are in the StateReserved
// state.
//
// NOTE: this is part of the Store interface.
func (db *BoltStore) DeleteReservedSessions() error {
return db.Update(func(tx *bbolt.Tx) error {
sessionBucket, err := getBucket(tx, sessionBucketKey)
if err != nil {
return err
}
return sessionBucket.ForEach(func(k, v []byte) error {
// We'll also get buckets here, skip those (identified
// by nil value).
if v == nil {
return nil
}
session, err := DeserializeSession(bytes.NewReader(v))
if err != nil {
return err
}
if session.State != StateReserved {
return nil
}
err = sessionBucket.Delete(k)
if err != nil {
return err
}
idIndexBkt := sessionBucket.Bucket(idIndexKey)
if idIndexBkt == nil {
return ErrDBInitErr
}
// Delete the entire session ID bucket.
err = idIndexBkt.DeleteBucket(session.ID[:])
if err != nil {
return err
}
groupIdIndexBkt := sessionBucket.Bucket(groupIDIndexKey)
if groupIdIndexBkt == nil {
return ErrDBInitErr
}
groupBkt := groupIdIndexBkt.Bucket(session.GroupID[:])
if groupBkt == nil {
return ErrDBInitErr
}
sessionIDsBkt := groupBkt.Bucket(sessionIDKey)
if sessionIDsBkt == nil {
return ErrDBInitErr
}
var (
seqKey []byte
numSessions int
)
err = sessionIDsBkt.ForEach(func(k, v []byte) error {
numSessions++
if !bytes.Equal(v, session.ID[:]) {
return nil
}
seqKey = k
return nil
})
if err != nil {
return err
}
if numSessions == 0 {
return fmt.Errorf("no sessions found for "+
"group ID %x", session.GroupID)
}
if numSessions == 1 {
// Delete the whole group bucket.
return groupBkt.DeleteBucket(sessionIDKey)
}
// Else, delete just the session ID entry.
return sessionIDsBkt.Delete(seqKey)
})
})
}
// RevokeSession updates the state of the session with the given local
// public key to be revoked.
//

View file

@ -142,9 +142,58 @@ func TestBasicSessionStore(t *testing.T) {
require.NoError(t, err)
require.Empty(t, sessions)
sessions, err = db.ListSessionsByState(StateInUse)
sessions, err = db.ListSessionsByState(StateReserved)
require.NoError(t, err)
require.Empty(t, sessions)
// Demonstrate deletion of a reserved session.
//
// Calling DeleteReservedSessions should have no effect yet since none
// of the sessions are reserved.
require.NoError(t, db.DeleteReservedSessions())
sessions, err = db.ListSessionsByState(StateReserved)
require.NoError(t, err)
require.Empty(t, sessions)
// Add a session and put it in the StateReserved state. We'll also
// link it to session 1.
s5 := newSession(
t, db, clock, "session 5", withState(StateReserved),
withLinkedGroupID(&session1.GroupID),
)
require.NoError(t, db.CreateSession(s5))
sessions, err = db.ListSessionsByState(StateReserved)
require.NoError(t, err)
require.Equal(t, 1, len(sessions))
assertEqualSessions(t, s5, sessions[0])
// Show that the group ID/session ID index has also been populated with
// this session.
groupID, err := db.GetGroupID(s5.ID)
require.NoError(t, err)
require.Equal(t, s1.ID, groupID)
sessIDs, err := db.GetSessionIDs(s5.GroupID)
require.NoError(t, err)
require.ElementsMatch(t, []ID{s5.ID, s1.ID}, sessIDs)
// Now delete the reserved session and show that it is no longer in the
// database and no longer in the group ID/session ID index.
require.NoError(t, db.DeleteReservedSessions())
sessions, err = db.ListSessionsByState(StateReserved)
require.NoError(t, err)
require.Empty(t, sessions)
_, err = db.GetGroupID(s5.ID)
require.ErrorContains(t, err, "no index entry")
// Only session 1 should remain in this group.
sessIDs, err = db.GetSessionIDs(s5.GroupID)
require.NoError(t, err)
require.ElementsMatch(t, []ID{s1.ID}, sessIDs)
}
// TestLinkingSessions tests that session linking works as expected.
@ -359,6 +408,12 @@ func withType(t Type) testSessionModifier {
}
}
func withState(state State) testSessionModifier {
return func(s *Session) {
s.State = state
}
}
func newSession(t *testing.T, db Store, clock clock.Clock, label string,
mods ...testSessionModifier) *Session {

View file

@ -100,6 +100,12 @@ func newSessionRPCServer(cfg *sessionRpcServerConfig) (*sessionRpcServer,
// start all the components necessary for the sessionRpcServer to start serving
// requests. This includes resuming all non-revoked sessions.
func (s *sessionRpcServer) start(ctx context.Context) error {
// Delete all sessions in the Reserved state.
err := s.cfg.db.DeleteReservedSessions()
if err != nil {
return fmt.Errorf("error deleting reserved sessions: %v", err)
}
// Start up all previously created sessions.
sessions, err := s.cfg.db.ListSessionsByState(
session.StateCreated,
@ -1518,6 +1524,9 @@ func marshalRPCMacaroonRecipe(
// marshalRPCState converts a session state to its RPC counterpart.
func marshalRPCState(state session.State) (litrpc.SessionState, error) {
switch state {
case session.StateReserved:
return litrpc.SessionState_STATE_RESERVED, nil
case session.StateCreated:
return litrpc.SessionState_STATE_CREATED, nil