diff --git a/session/interface.go b/session/interface.go index ad94a1f1c..7e84bf07e 100644 --- a/session/interface.go +++ b/session/interface.go @@ -120,10 +120,13 @@ type Session struct { // buildSession creates a new session with the given user-defined parameters. func buildSession(id ID, localPrivKey *btcec.PrivateKey, label string, typ Type, - created, expiry time.Time, serverAddr string, devServer bool, - perms []bakery.Op, caveats []macaroon.Caveat, - featureConfig FeaturesConfig, privacy bool, linkedGroupID *ID, - flags PrivacyFlags) (*Session, error) { + created, expiry time.Time, serverAddr string, + options ...Option) (*Session, error) { + + opts := defaultSessionOptions() + for _, o := range options { + o(opts) + } _, pairingSecret, err := mailbox.NewPassphraseEntropy() if err != nil { @@ -135,10 +138,10 @@ func buildSession(id ID, localPrivKey *btcec.PrivateKey, label string, typ Type, // The group ID will by default be the same as the Session ID // unless this session links to a previous session. groupID := id - if linkedGroupID != nil { + if opts.linkedGroupID != nil { // If this session is linked to a previous session, then the // group ID is the same as the linked session's group ID. - groupID = *linkedGroupID + groupID = *opts.linkedGroupID } sess := &Session{ @@ -149,29 +152,109 @@ func buildSession(id ID, localPrivKey *btcec.PrivateKey, label string, typ Type, Expiry: expiry.UTC(), CreatedAt: created.UTC(), ServerAddr: serverAddr, - DevServer: devServer, + DevServer: opts.devServer, MacaroonRootKey: macRootKey, PairingSecret: pairingSecret, LocalPrivateKey: localPrivKey, LocalPublicKey: localPrivKey.PubKey(), RemotePublicKey: nil, - WithPrivacyMapper: privacy, - PrivacyFlags: flags, + WithPrivacyMapper: opts.privacy, + PrivacyFlags: opts.privacyFlags, GroupID: groupID, + MacaroonRecipe: opts.macaroonRecipe, } - if perms != nil || caveats != nil { - sess.MacaroonRecipe = &MacaroonRecipe{ - Permissions: perms, - Caveats: caveats, - } + if len(opts.featureConfig) != 0 { + sess.FeatureConfig = &opts.featureConfig } - if len(featureConfig) != 0 { - sess.FeatureConfig = &featureConfig + return sess, nil +} + +// sessionOptions defines various options that can be tweaked via functional +// parameters for session creation. +type sessionOptions struct { + // privacy indicates if a privacy map should be used with this session. + privacy bool + + // privacyFlags to use in combination with the session's privacy mapper. + privacyFlags PrivacyFlags + + // featureConfig holds any feature configuration bytes to use for this + // session. + featureConfig FeaturesConfig + + // linkedGroupID is the ID of the group that this session is linked + // to. By default, a session is not linked to another group. + linkedGroupID *ID + + // devServer is true if TLS should be skipped when connecting to the + // mailbox server. + devServer bool + + // macaroonRecipe holds the permissions and caveats that should be used + // to bake the macaroon to be used with this session. + macaroonRecipe *MacaroonRecipe +} + +// defaultSessionOptions returns a new sessionOptions struct with default +// values set. +func defaultSessionOptions() *sessionOptions { + return &sessionOptions{ + privacy: false, + privacyFlags: PrivacyFlags{}, + featureConfig: FeaturesConfig{}, + linkedGroupID: nil, + devServer: false, } +} - return sess, nil +// Option defines the signature of a functional option that can be used to +// tweak various session creation options. +type Option func(*sessionOptions) + +// WithPrivacy can be used to enable the privacy mapper for this session. +// An optional set of privacy flags can be provided to further customize the +// privacy mapper. +func WithPrivacy(flags PrivacyFlags) Option { + return func(o *sessionOptions) { + o.privacy = true + o.privacyFlags = flags + } +} + +// WithFeatureConfig can be used to set the feature configuration bytes for +// this session. +func WithFeatureConfig(config FeaturesConfig) Option { + return func(o *sessionOptions) { + o.featureConfig = config + } +} + +// WithLinkedGroupID can be used to link this session to a previous session. +func WithLinkedGroupID(groupID *ID) Option { + return func(o *sessionOptions) { + o.linkedGroupID = groupID + } +} + +// WithDevServer can be used to set if TLS verification should be skipped when +// connecting to the mailbox server. +func WithDevServer() Option { + return func(o *sessionOptions) { + o.devServer = true + } +} + +// WithMacaroonRecipe can be used to set the permissions and caveats that +// should be used to bake the macaroon for a session. +func WithMacaroonRecipe(caveats []macaroon.Caveat, perms []bakery.Op) Option { + return func(o *sessionOptions) { + o.macaroonRecipe = &MacaroonRecipe{ + Permissions: perms, + Caveats: caveats, + } + } } // IDToGroupIndex defines an interface for the session ID to group ID index. @@ -191,9 +274,7 @@ type Store interface { // parameters. The session will remain in the StateReserved state until // ShiftState is called to update the state. NewSession(label string, typ Type, expiry time.Time, serverAddr string, - devServer bool, perms []bakery.Op, caveats []macaroon.Caveat, - featureConfig FeaturesConfig, privacy bool, linkedGroupID *ID, - flags PrivacyFlags) (*Session, error) + opts ...Option) (*Session, error) // GetSession fetches the session with the given key. GetSession(key *btcec.PublicKey) (*Session, error) diff --git a/session/kvdb_store.go b/session/kvdb_store.go index 84f4dce06..168effa74 100644 --- a/session/kvdb_store.go +++ b/session/kvdb_store.go @@ -13,8 +13,6 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/lightningnetwork/lnd/clock" "go.etcd.io/bbolt" - "gopkg.in/macaroon-bakery.v2/bakery" - "gopkg.in/macaroon.v2" ) var ( @@ -188,9 +186,7 @@ func getSessionKey(session *Session) []byte { // // NOTE: this is part of the Store interface. func (db *BoltStore) NewSession(label string, typ Type, expiry time.Time, - serverAddr string, devServer bool, perms []bakery.Op, - caveats []macaroon.Caveat, featureConfig FeaturesConfig, privacy bool, - linkedGroupID *ID, flags PrivacyFlags) (*Session, error) { + serverAddr string, opts ...Option) (*Session, error) { var session *Session err := db.Update(func(tx *bbolt.Tx) error { @@ -206,8 +202,7 @@ func (db *BoltStore) NewSession(label string, typ Type, expiry time.Time, session, err = buildSession( id, localPrivKey, label, typ, db.clock.Now(), expiry, - serverAddr, devServer, perms, caveats, featureConfig, - privacy, linkedGroupID, flags, + serverAddr, opts..., ) if err != nil { return err diff --git a/session/store_test.go b/session/store_test.go index aa7afe20f..e2b507300 100644 --- a/session/store_test.go +++ b/session/store_test.go @@ -374,8 +374,10 @@ func reserveSession(db Store, label string, return db.NewSession(label, opts.sessType, time.Date(99999, 1, 1, 0, 0, 0, 0, time.UTC), - "foo.bar.baz:1234", true, nil, nil, nil, true, opts.groupID, - []PrivacyFlag{ClearPubkeys}, + "foo.bar.baz:1234", + WithDevServer(), + WithPrivacy(PrivacyFlags{ClearPubkeys}), + WithLinkedGroupID(opts.groupID), ) } diff --git a/session/tlv_test.go b/session/tlv_test.go index 54362b0cb..9258aa7be 100644 --- a/session/tlv_test.go +++ b/session/tlv_test.go @@ -133,10 +133,12 @@ func TestSerializeDeserializeSession(t *testing.T) { id, priv, test.name, test.sessType, time.Now(), time.Date(99999, 1, 1, 0, 0, 0, 0, time.UTC), - "foo.bar.baz:1234", true, test.perms, - test.caveats, test.featureConfig, true, - test.linkedGroupID, - []PrivacyFlag{ClearPubkeys}, + "foo.bar.baz:1234", + WithDevServer(), + WithPrivacy(PrivacyFlags{ClearPubkeys}), + WithMacaroonRecipe(test.caveats, test.perms), + WithFeatureConfig(test.featureConfig), + WithLinkedGroupID(test.linkedGroupID), ) require.NoError(t, err) @@ -188,8 +190,8 @@ func TestGroupIDForOlderSessions(t *testing.T) { id, priv, "test-session", TypeMacaroonAdmin, time.Now(), time.Date(99999, 1, 1, 0, 0, 0, 0, time.UTC), - "foo.bar.baz:1234", true, nil, nil, nil, false, nil, - PrivacyFlags{}, + "foo.bar.baz:1234", + WithDevServer(), ) require.NoError(t, err) @@ -224,8 +226,8 @@ func TestGroupID(t *testing.T) { id, priv, "test-session", TypeMacaroonAdmin, time.Now(), time.Date(99999, 1, 1, 0, 0, 0, 0, time.UTC), - "foo.bar.baz:1234", true, nil, nil, nil, false, nil, - PrivacyFlags{}, + "foo.bar.baz:1234", + WithDevServer(), ) require.NoError(t, err) @@ -239,8 +241,9 @@ func TestGroupID(t *testing.T) { id, priv, "test-session", TypeMacaroonAdmin, time.Now(), time.Date(99999, 1, 1, 0, 0, 0, 0, time.UTC), - "foo.bar.baz:1234", true, nil, nil, nil, false, - &session1.GroupID, PrivacyFlags{}, + "foo.bar.baz:1234", + WithLinkedGroupID(&session1.ID), + WithDevServer(), ) require.NoError(t, err) diff --git a/session_rpcserver.go b/session_rpcserver.go index 778b593cc..46d8d162b 100644 --- a/session_rpcserver.go +++ b/session_rpcserver.go @@ -308,10 +308,16 @@ func (s *sessionRpcServer) AddSession(ctx context.Context, } } + sessOpts := []session.Option{ + session.WithMacaroonRecipe(caveats, uniquePermissions), + } + + if req.DevServer { + sessOpts = append(sessOpts, session.WithDevServer()) + } + sess, err := s.cfg.db.NewSession( - req.Label, typ, expiry, req.MailboxServerAddr, - req.DevServer, uniquePermissions, caveats, nil, false, nil, - session.PrivacyFlags{}, + req.Label, typ, expiry, req.MailboxServerAddr, sessOpts..., ) if err != nil { return nil, fmt.Errorf("error creating new session: %v", err) @@ -911,6 +917,11 @@ func (s *sessionRpcServer) AddAutopilotSession(ctx context.Context, // Determine privacy flags to use for session registration. var privacyFlags session.PrivacyFlags if req.PrivacyFlagsSet { + if !privacy { + return nil, fmt.Errorf("privacy flags can only be " + + "set when the privacy mapper is enabled") + } + // We apply privacy flags from the session request in order to // to be able to set flags resulting from non-standard feature // configurations. @@ -1120,10 +1131,25 @@ func (s *sessionRpcServer) AddAutopilotSession(ctx context.Context, caveats = append(caveats, firewall.MetaPrivacyCaveat) } + // Construct the functional options that will be used to create the + // session. + sessOpts := []session.Option{ + session.WithMacaroonRecipe(caveats, perms), + session.WithFeatureConfig(clientConfig), + session.WithLinkedGroupID(linkedGroupID), + } + + if req.DevServer { + sessOpts = append(sessOpts, session.WithDevServer()) + } + + if privacy { + sessOpts = append(sessOpts, session.WithPrivacy(privacyFlags)) + } + sess, err := s.cfg.db.NewSession( - req.Label, session.TypeAutopilot, expiry, - req.MailboxServerAddr, req.DevServer, perms, caveats, - clientConfig, privacy, linkedGroupID, privacyFlags, + req.Label, session.TypeAutopilot, expiry, req.MailboxServerAddr, + sessOpts..., ) if err != nil { return nil, fmt.Errorf("error creating new session: %v", err)