firewall+firewalldb: add SessionDB interface

SessionDB is an interface that gives helper methods for how privacy
mapping should be done. A mock for SessionDB is added to save on some
code repetition, the privacy flags functionality is used in a later
test.
This commit is contained in:
bitromortac 2024-04-23 10:32:05 +02:00
parent 1e83df2225
commit cb542a5015
No known key found for this signature in database
GPG key ID: 1965063FC13BEBE2
7 changed files with 130 additions and 112 deletions

View file

@ -59,21 +59,21 @@ var _ mid.RequestInterceptor = (*PrivacyMapper)(nil)
// PrivacyMapper is a RequestInterceptor that maps any pseudo names in certain
// requests to their real values and vice versa for responses.
type PrivacyMapper struct {
newDB firewalldb.NewPrivacyMapDB
randIntn func(int) (int, error)
sessionIDIndexDB session.IDToGroupIndex
newDB firewalldb.NewPrivacyMapDB
randIntn func(int) (int, error)
sessionDB firewalldb.SessionDB
}
// NewPrivacyMapper returns a new instance of PrivacyMapper. The randIntn
// function is used to draw randomness for request field obfuscation.
func NewPrivacyMapper(newDB firewalldb.NewPrivacyMapDB,
randIntn func(int) (int, error),
sessionIDIndexDB session.IDToGroupIndex) *PrivacyMapper {
sessionDB firewalldb.SessionDB) *PrivacyMapper {
return &PrivacyMapper{
newDB: newDB,
randIntn: randIntn,
sessionIDIndexDB: sessionIDIndexDB,
newDB: newDB,
randIntn: randIntn,
sessionDB: sessionDB,
}
}
@ -111,7 +111,7 @@ func (p *PrivacyMapper) Intercept(ctx context.Context,
}
// Get group ID for session ID.
groupID, err := p.sessionIDIndexDB.GetGroupID(sessionID)
groupID, err := p.sessionDB.GetGroupID(sessionID)
if err != nil {
return nil, err
}

View file

@ -3,7 +3,6 @@ package firewall
import (
"context"
"encoding/json"
"fmt"
"testing"
"time"
@ -292,17 +291,18 @@ func TestPrivacyMapper(t *testing.T) {
"01020304": "c8134495",
}
db := newMockDB(t, mapPreloadRealToPseudo, sessionID)
err = db.AddSessionAndGroupIDPair(sessionID, sessionID)
require.NoError(t, err)
// randIntn is used for deterministic testing.
randIntn := func(n int) (int, error) { return 100, nil }
p := NewPrivacyMapper(db.NewSessionDB, randIntn, db)
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
// Initialize privacy mapping.
db := newMockDB(t, mapPreloadRealToPseudo, sessionID)
pd := firewalldb.NewMockSessionDB()
pd.AddPair(sessionID, sessionID)
// randIntn is used for deterministic testing.
randIntn := func(n int) (int, error) { return 100, nil }
p := NewPrivacyMapper(db.NewSessionDB, randIntn, pd)
rawMsg, err := proto.Marshal(test.msg)
require.NoError(t, err)
@ -341,6 +341,12 @@ func TestPrivacyMapper(t *testing.T) {
// Subtest to test behavior with real randomness.
t.Run("Response with randomness", func(t *testing.T) {
// Initialize privacy mapping.
db := newMockDB(t, mapPreloadRealToPseudo, sessionID)
pd := firewalldb.NewMockSessionDB()
pd.AddPair(sessionID, sessionID)
msg := &lnrpc.ForwardingHistoryResponse{
ForwardingEvents: []*lnrpc.ForwardingEvent{
{
@ -360,7 +366,7 @@ func TestPrivacyMapper(t *testing.T) {
rawMsg, err := proto.Marshal(msg)
require.NoError(t, err)
p = NewPrivacyMapper(db.NewSessionDB, CryptoRandIntn, db)
p := NewPrivacyMapper(db.NewSessionDB, CryptoRandIntn, pd)
require.NoError(t, err)
// We test the independent outgoing amount (incoming amount
@ -373,7 +379,7 @@ func TestPrivacyMapper(t *testing.T) {
// We keep track of the timestamp. We test only the timestamp in
// seconds as there can be numerical inaccuracies with the
// nanosecond one.
timestamp := msg.ForwardingEvents[0].Timestamp
timestamp := msg.ForwardingEvents[0].TimestampNs / 1e9
timestampInterval := uint64(timeVariation) / 1e9
minTime := timestamp - timestampInterval
maxTime := timestamp + timestampInterval
@ -447,19 +453,12 @@ func TestPrivacyMapper(t *testing.T) {
type mockDB struct {
privDB map[string]*mockPrivacyMapDB
sessionIDIndex map[session.ID]session.ID
groupIDIndex map[session.ID][]session.ID
}
func newMockDB(t *testing.T, preloadRealToPseudo map[string]string,
sessID session.ID) mockDB {
db := mockDB{
privDB: make(map[string]*mockPrivacyMapDB),
sessionIDIndex: make(map[session.ID]session.ID),
groupIDIndex: make(map[session.ID][]session.ID),
}
db := mockDB{privDB: make(map[string]*mockPrivacyMapDB)}
sessDB := db.NewSessionDB(sessID)
_ = sessDB.Update(func(tx firewalldb.PrivacyMapTx) error {
@ -484,30 +483,6 @@ func (m mockDB) NewSessionDB(sessionID session.ID) firewalldb.PrivacyMapDB {
return newDB
}
func (m mockDB) AddSessionAndGroupIDPair(sessionID, groupID session.ID) error {
m.sessionIDIndex[sessionID] = groupID
m.groupIDIndex[groupID] = append(m.groupIDIndex[groupID], sessionID)
return nil
}
func (m mockDB) GetGroupID(sessionID session.ID) (session.ID, error) {
groupID, ok := m.sessionIDIndex[sessionID]
if !ok {
return session.ID{}, fmt.Errorf("group ID not found")
}
return groupID, nil
}
func (m mockDB) GetSessionIDs(groupID session.ID) ([]session.ID, error) {
sessionIDs, ok := m.groupIDIndex[groupID]
if !ok {
return nil, fmt.Errorf("group ID not found")
}
return sessionIDs, nil
}
func newMockPrivacyMapDB() *mockPrivacyMapDB {
return &mockPrivacyMapDB{
r2p: make(map[string]string),

View file

@ -30,7 +30,7 @@ var _ mid.RequestInterceptor = (*RuleEnforcer)(nil)
type RuleEnforcer struct {
ruleDB firewalldb.RulesDB
actionsDB firewalldb.ActionReadDBGetter
sessionIDIndexDB session.IDToGroupIndex
sessionDB firewalldb.SessionDB
markActionErrored func(reqID uint64, reason string) error
newPrivMap firewalldb.NewPrivacyMapDB
@ -52,7 +52,7 @@ type featurePerms func(ctx context.Context) (map[string]map[string]bool, error)
// NewRuleEnforcer constructs a new RuleEnforcer instance.
func NewRuleEnforcer(ruleDB firewalldb.RulesDB,
actionsDB firewalldb.ActionReadDBGetter,
sessionIDIndex session.IDToGroupIndex,
sessionIDIndex firewalldb.SessionDB,
getFeaturePerms featurePerms, permsMgr *perms.Manager, nodeID [33]byte,
routerClient lndclient.RouterClient,
lndClient lndclient.LightningClient, ruleMgrs rules.ManagerSet,
@ -70,7 +70,7 @@ func NewRuleEnforcer(ruleDB firewalldb.RulesDB,
ruleMgrs: ruleMgrs,
markActionErrored: markActionErrored,
newPrivMap: privMap,
sessionIDIndexDB: sessionIDIndex,
sessionDB: sessionIDIndex,
}
}
@ -224,7 +224,7 @@ func (r *RuleEnforcer) handleRequest(ctx context.Context,
return nil, fmt.Errorf("could not extract ID from macaroon")
}
groupID, err := r.sessionIDIndexDB.GetGroupID(sessionID)
groupID, err := r.sessionDB.GetGroupID(sessionID)
if err != nil {
return nil, err
}
@ -269,7 +269,7 @@ func (r *RuleEnforcer) handleResponse(ctx context.Context,
return nil, fmt.Errorf("could not extract ID from macaroon")
}
groupID, err := r.sessionIDIndexDB.GetGroupID(sessionID)
groupID, err := r.sessionDB.GetGroupID(sessionID)
if err != nil {
return nil, err
}
@ -308,7 +308,7 @@ func (r *RuleEnforcer) handleErrorResponse(ctx context.Context,
return nil, fmt.Errorf("could not extract ID from macaroon")
}
groupID, err := r.sessionIDIndexDB.GetGroupID(sessionID)
groupID, err := r.sessionDB.GetGroupID(sessionID)
if err != nil {
return nil, err
}

View file

@ -5,7 +5,6 @@ import (
"testing"
"time"
"github.com/lightninglabs/lightning-terminal/session"
"github.com/stretchr/testify/require"
)
@ -346,9 +345,9 @@ func TestListGroupActions(t *testing.T) {
group1 := intToSessionID(0)
// Link session 1 and session 2 to group 1.
index := newMockSessionIDIndex()
index.addPair(sessionID1, group1)
index.addPair(sessionID2, group1)
index := NewMockSessionDB()
index.AddPair(sessionID1, group1)
index.AddPair(sessionID2, group1)
db, err := NewDB(t.TempDir(), "test.db", index)
require.NoError(t, err)
@ -382,48 +381,3 @@ func TestListGroupActions(t *testing.T) {
require.Equal(t, sessionID1, al[0].SessionID)
require.Equal(t, sessionID2, al[1].SessionID)
}
type mockSessionIDIndex struct {
sessionToGroupID map[session.ID]session.ID
groupToSessionIDs map[session.ID][]session.ID
}
var _ session.IDToGroupIndex = (*mockSessionIDIndex)(nil)
func newMockSessionIDIndex() *mockSessionIDIndex {
return &mockSessionIDIndex{
sessionToGroupID: make(map[session.ID]session.ID),
groupToSessionIDs: make(map[session.ID][]session.ID),
}
}
func (m *mockSessionIDIndex) addPair(sessionID, groupID session.ID) {
m.sessionToGroupID[sessionID] = groupID
m.groupToSessionIDs[groupID] = append(
m.groupToSessionIDs[groupID], sessionID,
)
}
func (m *mockSessionIDIndex) GetGroupID(sessionID session.ID) (session.ID,
error) {
id, ok := m.sessionToGroupID[sessionID]
if !ok {
return session.ID{}, fmt.Errorf("no group ID found for " +
"session ID")
}
return id, nil
}
func (m *mockSessionIDIndex) GetSessionIDs(groupID session.ID) ([]session.ID,
error) {
ids, ok := m.groupToSessionIDs[groupID]
if !ok {
return nil, fmt.Errorf("no session IDs found for group ID")
}
return ids, nil
}

View file

@ -8,7 +8,6 @@ import (
"path/filepath"
"time"
"github.com/lightninglabs/lightning-terminal/session"
"go.etcd.io/bbolt"
)
@ -42,13 +41,11 @@ var (
type DB struct {
*bbolt.DB
sessionIDIndex session.IDToGroupIndex
sessionIDIndex SessionDB
}
// NewDB creates a new bolt database that can be found at the given directory.
func NewDB(dir, fileName string, sessionIDIndex session.IDToGroupIndex) (*DB,
error) {
func NewDB(dir, fileName string, sessionIDIndex SessionDB) (*DB, error) {
firstInit := false
path := filepath.Join(dir, fileName)

12
firewalldb/interface.go Normal file
View file

@ -0,0 +1,12 @@
package firewalldb
import "github.com/lightninglabs/lightning-terminal/session"
// SessionDB is an interface that abstracts the database operations needed for
// the privacy mapper to function.
type SessionDB interface {
session.IDToGroupIndex
// GetSessionByID returns the session for a specific id.
GetSessionByID(session.ID) (*session.Session, error)
}

80
firewalldb/mock.go Normal file
View file

@ -0,0 +1,80 @@
package firewalldb
import (
"fmt"
"github.com/lightninglabs/lightning-terminal/session"
)
type mockSessionDB struct {
sessionToGroupID map[session.ID]session.ID
groupToSessionIDs map[session.ID][]session.ID
privacyFlags map[session.ID]session.PrivacyFlags
}
var _ SessionDB = (*mockSessionDB)(nil)
// NewMockSessionDB creates a new mock privacy map details instance.
func NewMockSessionDB() *mockSessionDB {
return &mockSessionDB{
sessionToGroupID: make(map[session.ID]session.ID),
groupToSessionIDs: make(map[session.ID][]session.ID),
privacyFlags: make(map[session.ID]session.PrivacyFlags),
}
}
// AddPair adds a new session to group ID pair to the mock details.
func (m *mockSessionDB) AddPair(sessionID, groupID session.ID) {
m.sessionToGroupID[sessionID] = groupID
m.groupToSessionIDs[groupID] = append(
m.groupToSessionIDs[groupID], sessionID,
)
}
// GetGroupID returns the group ID for the given session ID.
func (m *mockSessionDB) GetGroupID(sessionID session.ID) (session.ID, error) {
id, ok := m.sessionToGroupID[sessionID]
if !ok {
return session.ID{}, fmt.Errorf("no group ID found for " +
"session ID")
}
return id, nil
}
// GetSessionIDs returns the set of session IDs that are in the group
func (m *mockSessionDB) GetSessionIDs(groupID session.ID) ([]session.ID, error) {
ids, ok := m.groupToSessionIDs[groupID]
if !ok {
return nil, fmt.Errorf("no session IDs found for group ID")
}
return ids, nil
}
// GetSessionByID returns the session for a specific id.
func (m *mockSessionDB) GetSessionByID(sessionID session.ID) (*session.Session,
error) {
s, ok := m.sessionToGroupID[sessionID]
if !ok {
return nil, fmt.Errorf("no session found for session ID")
}
f, ok := m.privacyFlags[sessionID]
if !ok {
return nil, fmt.Errorf("no privacy flags found for session ID")
}
return &session.Session{GroupID: s, PrivacyFlags: f}, nil
}
// AddPrivacyFlags is a helper that adds privacy flags to the mock session db.
func (m *mockSessionDB) AddPrivacyFlags(sessionID session.ID,
flags session.PrivacyFlags) error {
m.privacyFlags[sessionID] = flags
return nil
}