Skip to content

Commit c098e54

Browse files
committed
session: introduce Reserve->Create pattern
TODO: flesh out commit message
1 parent fafbbed commit c098e54

File tree

4 files changed

+205
-207
lines changed

4 files changed

+205
-207
lines changed

session/interface.go

Lines changed: 17 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,15 @@ const (
2727
type State uint8
2828

2929
/*
30-
/---> StateExpired
31-
StateCreated ---
32-
\---> StateRevoked
30+
/---> StateExpired
31+
StateReserved ---> StateCreated ---
32+
\---> StateRevoked
3333
*/
3434

3535
const (
3636
// StateCreated is the state of a session once it has been fully
37-
// committed to the Store and is ready to be used. This is the first
38-
// state of a session.
37+
// committed to the BoltStore and is ready to be used. This is the
38+
// first state after StateReserved.
3939
StateCreated State = 0
4040

4141
// StateInUse is the state of a session that is currently being used.
@@ -52,10 +52,10 @@ const (
5252
// date.
5353
StateExpired State = 3
5454

55-
// StateReserved is a temporary initial state of a session. On start-up,
56-
// any sessions in this state should be cleaned up.
57-
//
58-
// NOTE: this isn't used yet.
55+
// StateReserved is a temporary initial state of a session. This is used
56+
// to reserve a unique ID and private key pair for a session before it
57+
// is fully created. On start-up, any sessions in this state should be
58+
// cleaned up.
5959
StateReserved State = 4
6060
)
6161

@@ -127,7 +127,7 @@ func buildSession(id ID, localPrivKey *btcec.PrivateKey, label string, typ Type,
127127
sess := &Session{
128128
ID: id,
129129
Label: label,
130-
State: StateCreated,
130+
State: StateReserved,
131131
Type: typ,
132132
Expiry: expiry.UTC(),
133133
CreatedAt: created.UTC(),
@@ -171,22 +171,16 @@ type IDToGroupIndex interface {
171171
// retrieving Terminal Connect sessions.
172172
type Store interface {
173173
// NewSession creates a new session with the given user-defined
174-
// parameters.
175-
//
176-
// NOTE: currently this purely a constructor of the Session type and
177-
// does not make any database calls. This will be changed in a future
178-
// commit.
179-
NewSession(id ID, localPrivKey *btcec.PrivateKey, label string,
180-
typ Type, expiry time.Time, serverAddr string, devServer bool,
181-
perms []bakery.Op, caveats []macaroon.Caveat,
174+
// parameters. The session will remain in the StateReserved state until
175+
// CreateSession is called for the session.
176+
NewSession(label string, typ Type, expiry time.Time, serverAddr string,
177+
devServer bool, perms []bakery.Op, caveats []macaroon.Caveat,
182178
featureConfig FeaturesConfig, privacy bool, linkedGroupID *ID,
183179
flags PrivacyFlags) (*Session, error)
184180

185-
// CreateSession adds a new session to the store. If a session with the
186-
// same local public key already exists an error is returned. This
187-
// can only be called with a Session with an ID that the Store has
188-
// reserved.
189-
CreateSession(*Session) error
181+
// CreateSession moves the given session from the StateReserved state to
182+
// the StateCreated state.
183+
CreateSession(ID) (*Session, error)
190184

191185
// GetSession fetches the session with the given key.
192186
GetSession(key *btcec.PublicKey) (*Session, error)
@@ -208,12 +202,6 @@ type Store interface {
208202
UpdateSessionRemotePubKey(localPubKey,
209203
remotePubKey *btcec.PublicKey) error
210204

211-
// GetUnusedIDAndKeyPair can be used to generate a new, unused, local
212-
// private key and session ID pair. Care must be taken to ensure that no
213-
// other thread calls this before the returned ID and key pair from this
214-
// method are either used or discarded.
215-
GetUnusedIDAndKeyPair() (ID, *btcec.PrivateKey, error)
216-
217205
// GetSessionByID fetches the session with the given ID.
218206
GetSessionByID(id ID) (*Session, error)
219207

session/store.go

Lines changed: 81 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -69,35 +69,34 @@ func getSessionKey(session *Session) []byte {
6969
// make any database calls. This will be changed in a future commit.
7070
//
7171
// NOTE: this is part of the Store interface.
72-
func (db *DB) NewSession(id ID, localPrivKey *btcec.PrivateKey, label string,
73-
typ Type, expiry time.Time, serverAddr string, devServer bool,
74-
perms []bakery.Op, caveats []macaroon.Caveat,
75-
featureConfig FeaturesConfig, privacy bool, linkedGroupID *ID,
76-
flags PrivacyFlags) (*Session, error) {
77-
78-
return buildSession(
79-
id, localPrivKey, label, typ, db.clock.Now(), expiry,
80-
serverAddr, devServer, perms, caveats, featureConfig, privacy,
81-
linkedGroupID, flags,
82-
)
83-
}
84-
85-
// CreateSession adds a new session to the store. If a session with the same
86-
// local public key already exists an error is returned.
87-
//
88-
// NOTE: this is part of the Store interface.
89-
func (db *DB) CreateSession(session *Session) error {
90-
sessionKey := getSessionKey(session)
72+
func (db *DB) NewSession(label string, typ Type, expiry time.Time,
73+
serverAddr string, devServer bool, perms []bakery.Op,
74+
caveats []macaroon.Caveat, featureConfig FeaturesConfig, privacy bool,
75+
linkedGroupID *ID, flags PrivacyFlags) (*Session, error) {
9176

92-
return db.Update(func(tx *bbolt.Tx) error {
77+
var session *Session
78+
err := db.Update(func(tx *bbolt.Tx) error {
9379
sessionBucket, err := getBucket(tx, sessionBucketKey)
9480
if err != nil {
9581
return err
9682
}
9783

84+
id, localPrivKey, err := getUnusedIDAndKeyPair(sessionBucket)
85+
if err != nil {
86+
return err
87+
}
88+
89+
session, err = buildSession(
90+
id, localPrivKey, label, typ, db.clock.Now(), expiry,
91+
serverAddr, devServer, perms, caveats, featureConfig,
92+
privacy, linkedGroupID, flags,
93+
)
94+
95+
sessionKey := getSessionKey(session)
96+
9897
if len(sessionBucket.Get(sessionKey)) != 0 {
99-
return fmt.Errorf("session with local public "+
100-
"key(%x) already exists",
98+
return fmt.Errorf("session with local public key(%x) "+
99+
"already exists",
101100
session.LocalPublicKey.SerializeCompressed())
102101
}
103102

@@ -156,6 +155,46 @@ func (db *DB) CreateSession(session *Session) error {
156155

157156
return putSession(sessionBucket, session)
158157
})
158+
if err != nil {
159+
return nil, err
160+
}
161+
162+
return session, nil
163+
}
164+
165+
// CreateSession moves the session with the given ID from the Reserved state to
166+
// the Created state.
167+
//
168+
// NOTE: this is part of the Store interface.
169+
func (db *DB) CreateSession(id ID) (*Session, error) {
170+
var session *Session
171+
err := db.Update(func(tx *bbolt.Tx) error {
172+
sessionBucket, err := getBucket(tx, sessionBucketKey)
173+
if err != nil {
174+
return err
175+
}
176+
177+
session, err = getSessionByID(sessionBucket, id)
178+
if err != nil {
179+
return err
180+
}
181+
182+
// The session MUST be in the Reserved state.
183+
if session.State != StateReserved {
184+
return fmt.Errorf("session must be in the Reserved " +
185+
"state for it to move to the Created state")
186+
}
187+
188+
// Move the session to the CreatedState.
189+
session.State = StateCreated
190+
191+
return putSession(sessionBucket, session)
192+
})
193+
if err != nil {
194+
return nil, err
195+
}
196+
197+
return session, nil
159198
}
160199

161200
// UpdateSessionRemotePubKey can be used to add the given remote pub key
@@ -439,53 +478,35 @@ func (db *DB) GetSessionByID(id ID) (*Session, error) {
439478
return session, nil
440479
}
441480

442-
// GetUnusedIDAndKeyPair can be used to generate a new, unused, local private
481+
// getUnusedIDAndKeyPair can be used to generate a new, unused, local private
443482
// key and session ID pair. Care must be taken to ensure that no other thread
444483
// calls this before the returned ID and key pair from this method are either
445484
// used or discarded.
446-
//
447-
// NOTE: this is part of the Store interface.
448-
func (db *DB) GetUnusedIDAndKeyPair() (ID, *btcec.PrivateKey, error) {
449-
var (
450-
id ID
451-
privKey *btcec.PrivateKey
452-
)
453-
err := db.Update(func(tx *bbolt.Tx) error {
454-
sessionBucket, err := getBucket(tx, sessionBucketKey)
455-
if err != nil {
456-
return err
457-
}
458-
459-
idIndexBkt := sessionBucket.Bucket(idIndexKey)
460-
if idIndexBkt == nil {
461-
return ErrDBInitErr
462-
}
485+
func getUnusedIDAndKeyPair(bucket *bbolt.Bucket) (ID, *btcec.PrivateKey,
486+
error) {
463487

464-
// Spin until we find a key with an ID that does not collide
465-
// with any of our existing IDs.
466-
for {
467-
// Generate a new private key and ID pair.
468-
privKey, id, err = NewSessionPrivKeyAndID()
469-
if err != nil {
470-
return err
471-
}
488+
idIndexBkt := bucket.Bucket(idIndexKey)
489+
if idIndexBkt == nil {
490+
return ID{}, nil, ErrDBInitErr
491+
}
472492

473-
// Check that no such ID exits in our id-to-key index.
474-
idBkt := idIndexBkt.Bucket(id[:])
475-
if idBkt != nil {
476-
continue
477-
}
493+
// Spin until we find a key with an ID that does not collide with any of
494+
// our existing IDs.
495+
for {
496+
// Generate a new private key and ID pair.
497+
privKey, id, err := NewSessionPrivKeyAndID()
498+
if err != nil {
499+
return ID{}, nil, err
500+
}
478501

479-
break
502+
// Check that no such ID exits in our id-to-key index.
503+
idBkt := idIndexBkt.Bucket(id[:])
504+
if idBkt != nil {
505+
continue
480506
}
481507

482-
return nil
483-
})
484-
if err != nil {
485-
return id, nil, err
508+
return id, privKey, nil
486509
}
487-
488-
return id, privKey, nil
489510
}
490511

491512
// GetGroupID will return the group ID for the given session ID.

0 commit comments

Comments
 (0)