Skip to content

Commit 4c87858

Browse files
committed
session: introduce Reserve->Create pattern
TODO: flesh out commit message
1 parent f62f3fb commit 4c87858

File tree

4 files changed

+208
-211
lines changed

4 files changed

+208
-211
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

@@ -123,7 +123,7 @@ func buildSession(id ID, localPrivKey *btcec.PrivateKey, label string, typ Type,
123123
sess := &Session{
124124
ID: id,
125125
Label: label,
126-
State: StateCreated,
126+
State: StateReserved,
127127
Type: typ,
128128
Expiry: expiry.UTC(),
129129
CreatedAt: created.UTC(),
@@ -167,22 +167,16 @@ type IDToGroupIndex interface {
167167
// retrieving Terminal Connect sessions.
168168
type Store interface {
169169
// NewSession creates a new session with the given user-defined
170-
// parameters.
171-
//
172-
// NOTE: currently this purely a constructor of the Session type and
173-
// does not make any database calls. This will be changed in a future
174-
// commit.
175-
NewSession(id ID, localPrivKey *btcec.PrivateKey, label string,
176-
typ Type, expiry time.Time, serverAddr string, devServer bool,
177-
perms []bakery.Op, caveats []macaroon.Caveat,
170+
// parameters. The session will remain in the StateReserved state until
171+
// CreateSession is called for the session.
172+
NewSession(label string, typ Type, expiry time.Time, serverAddr string,
173+
devServer bool, perms []bakery.Op, caveats []macaroon.Caveat,
178174
featureConfig FeaturesConfig, privacy bool, linkedGroupID *ID,
179175
flags PrivacyFlags) (*Session, error)
180176

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

187181
// GetSession fetches the session with the given key.
188182
GetSession(key *btcec.PublicKey) (*Session, error)
@@ -206,12 +200,6 @@ type Store interface {
206200
UpdateSessionRemotePubKey(localPubKey,
207201
remotePubKey *btcec.PublicKey) error
208202

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

session/kvdb_store.go

Lines changed: 84 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -182,41 +182,39 @@ func getSessionKey(session *Session) []byte {
182182
return session.LocalPublicKey.SerializeCompressed()
183183
}
184184

185-
// NewSession creates a new session with the given user-defined parameters.
186-
//
187-
// NOTE: currently this purely a constructor of the Session type and does not
188-
// make any database calls. This will be changed in a future commit.
185+
// NewSession creates and persists a new session with the given user-defined
186+
// parameters. The initial state of the session will be Reserved until
187+
// CreateSession is called.
189188
//
190189
// NOTE: this is part of the Store interface.
191-
func (db *BoltStore) NewSession(id ID, localPrivKey *btcec.PrivateKey,
192-
label string, typ Type, expiry time.Time, serverAddr string,
193-
devServer bool, perms []bakery.Op, caveats []macaroon.Caveat,
194-
featureConfig FeaturesConfig, privacy bool, linkedGroupID *ID,
195-
flags PrivacyFlags) (*Session, error) {
196-
197-
return buildSession(
198-
id, localPrivKey, label, typ, db.clock.Now(), expiry,
199-
serverAddr, devServer, perms, caveats, featureConfig, privacy,
200-
linkedGroupID, flags,
201-
)
202-
}
190+
func (db *BoltStore) NewSession(label string, typ Type, expiry time.Time,
191+
serverAddr string, devServer bool, perms []bakery.Op,
192+
caveats []macaroon.Caveat, featureConfig FeaturesConfig, privacy bool,
193+
linkedGroupID *ID, flags PrivacyFlags) (*Session, error) {
203194

204-
// CreateSession adds a new session to the store. If a session with the same
205-
// local public key already exists an error is returned.
206-
//
207-
// NOTE: this is part of the Store interface.
208-
func (db *BoltStore) CreateSession(session *Session) error {
209-
sessionKey := getSessionKey(session)
210-
211-
return db.Update(func(tx *bbolt.Tx) error {
195+
var session *Session
196+
err := db.Update(func(tx *bbolt.Tx) error {
212197
sessionBucket, err := getBucket(tx, sessionBucketKey)
213198
if err != nil {
214199
return err
215200
}
216201

202+
id, localPrivKey, err := getUnusedIDAndKeyPair(sessionBucket)
203+
if err != nil {
204+
return err
205+
}
206+
207+
session, err = buildSession(
208+
id, localPrivKey, label, typ, db.clock.Now(), expiry,
209+
serverAddr, devServer, perms, caveats, featureConfig,
210+
privacy, linkedGroupID, flags,
211+
)
212+
213+
sessionKey := getSessionKey(session)
214+
217215
if len(sessionBucket.Get(sessionKey)) != 0 {
218-
return fmt.Errorf("session with local public "+
219-
"key(%x) already exists",
216+
return fmt.Errorf("session with local public key(%x) "+
217+
"already exists",
220218
session.LocalPublicKey.SerializeCompressed())
221219
}
222220

@@ -275,6 +273,46 @@ func (db *BoltStore) CreateSession(session *Session) error {
275273

276274
return putSession(sessionBucket, session)
277275
})
276+
if err != nil {
277+
return nil, err
278+
}
279+
280+
return session, nil
281+
}
282+
283+
// CreateSession moves the session with the given ID from the Reserved state to
284+
// the Created state.
285+
//
286+
// NOTE: this is part of the Store interface.
287+
func (db *BoltStore) CreateSession(id ID) (*Session, error) {
288+
var session *Session
289+
err := db.Update(func(tx *bbolt.Tx) error {
290+
sessionBucket, err := getBucket(tx, sessionBucketKey)
291+
if err != nil {
292+
return err
293+
}
294+
295+
session, err = getSessionByID(sessionBucket, id)
296+
if err != nil {
297+
return err
298+
}
299+
300+
// The session MUST be in the Reserved state.
301+
if session.State != StateReserved {
302+
return fmt.Errorf("session must be in the Reserved " +
303+
"state for it to move to the Created state")
304+
}
305+
306+
// Move the session to the CreatedState.
307+
session.State = StateCreated
308+
309+
return putSession(sessionBucket, session)
310+
})
311+
if err != nil {
312+
return nil, err
313+
}
314+
315+
return session, nil
278316
}
279317

280318
// UpdateSessionRemotePubKey can be used to add the given remote pub key
@@ -565,53 +603,35 @@ func (db *BoltStore) GetSessionByID(id ID) (*Session, error) {
565603
return session, nil
566604
}
567605

568-
// GetUnusedIDAndKeyPair can be used to generate a new, unused, local private
606+
// getUnusedIDAndKeyPair can be used to generate a new, unused, local private
569607
// key and session ID pair. Care must be taken to ensure that no other thread
570608
// calls this before the returned ID and key pair from this method are either
571609
// used or discarded.
572-
//
573-
// NOTE: this is part of the Store interface.
574-
func (db *BoltStore) GetUnusedIDAndKeyPair() (ID, *btcec.PrivateKey, error) {
575-
var (
576-
id ID
577-
privKey *btcec.PrivateKey
578-
)
579-
err := db.Update(func(tx *bbolt.Tx) error {
580-
sessionBucket, err := getBucket(tx, sessionBucketKey)
581-
if err != nil {
582-
return err
583-
}
584-
585-
idIndexBkt := sessionBucket.Bucket(idIndexKey)
586-
if idIndexBkt == nil {
587-
return ErrDBInitErr
588-
}
610+
func getUnusedIDAndKeyPair(bucket *bbolt.Bucket) (ID, *btcec.PrivateKey,
611+
error) {
589612

590-
// Spin until we find a key with an ID that does not collide
591-
// with any of our existing IDs.
592-
for {
593-
// Generate a new private key and ID pair.
594-
privKey, id, err = NewSessionPrivKeyAndID()
595-
if err != nil {
596-
return err
597-
}
613+
idIndexBkt := bucket.Bucket(idIndexKey)
614+
if idIndexBkt == nil {
615+
return ID{}, nil, ErrDBInitErr
616+
}
598617

599-
// Check that no such ID exits in our id-to-key index.
600-
idBkt := idIndexBkt.Bucket(id[:])
601-
if idBkt != nil {
602-
continue
603-
}
618+
// Spin until we find a key with an ID that does not collide with any of
619+
// our existing IDs.
620+
for {
621+
// Generate a new private key and ID pair.
622+
privKey, id, err := NewSessionPrivKeyAndID()
623+
if err != nil {
624+
return ID{}, nil, err
625+
}
604626

605-
break
627+
// Check that no such ID exits in our id-to-key index.
628+
idBkt := idIndexBkt.Bucket(id[:])
629+
if idBkt != nil {
630+
continue
606631
}
607632

608-
return nil
609-
})
610-
if err != nil {
611-
return id, nil, err
633+
return id, privKey, nil
612634
}
613-
614-
return id, privKey, nil
615635
}
616636

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

0 commit comments

Comments
 (0)