Skip to content

Commit 1980990

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

File tree

4 files changed

+211
-211
lines changed

4 files changed

+211
-211
lines changed

session/interface.go

+17-29
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

+87-64
Original file line numberDiff line numberDiff line change
@@ -182,41 +182,42 @@ 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.
189-
//
190-
// 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-
}
203-
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.
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.
206188
//
207189
// NOTE: this is part of the Store interface.
208-
func (db *BoltStore) CreateSession(session *Session) error {
209-
sessionKey := getSessionKey(session)
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) {
210194

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+
if err != nil {
213+
return err
214+
}
215+
216+
sessionKey := getSessionKey(session)
217+
217218
if len(sessionBucket.Get(sessionKey)) != 0 {
218-
return fmt.Errorf("session with local public "+
219-
"key(%x) already exists",
219+
return fmt.Errorf("session with local public key(%x) "+
220+
"already exists",
220221
session.LocalPublicKey.SerializeCompressed())
221222
}
222223

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

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

280321
// UpdateSessionRemotePubKey can be used to add the given remote pub key
@@ -565,53 +606,35 @@ func (db *BoltStore) GetSessionByID(id ID) (*Session, error) {
565606
return session, nil
566607
}
567608

568-
// GetUnusedIDAndKeyPair can be used to generate a new, unused, local private
609+
// getUnusedIDAndKeyPair can be used to generate a new, unused, local private
569610
// key and session ID pair. Care must be taken to ensure that no other thread
570611
// calls this before the returned ID and key pair from this method are either
571612
// 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-
}
613+
func getUnusedIDAndKeyPair(bucket *bbolt.Bucket) (ID, *btcec.PrivateKey,
614+
error) {
589615

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-
}
616+
idIndexBkt := bucket.Bucket(idIndexKey)
617+
if idIndexBkt == nil {
618+
return ID{}, nil, ErrDBInitErr
619+
}
598620

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-
}
621+
// Spin until we find a key with an ID that does not collide with any of
622+
// our existing IDs.
623+
for {
624+
// Generate a new private key and ID pair.
625+
privKey, id, err := NewSessionPrivKeyAndID()
626+
if err != nil {
627+
return ID{}, nil, err
628+
}
604629

605-
break
630+
// Check that no such ID exits in our id-to-key index.
631+
idBkt := idIndexBkt.Bucket(id[:])
632+
if idBkt != nil {
633+
continue
606634
}
607635

608-
return nil
609-
})
610-
if err != nil {
611-
return id, nil, err
636+
return id, privKey, nil
612637
}
613-
614-
return id, privKey, nil
615638
}
616639

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

0 commit comments

Comments
 (0)