Skip to content

[sql-31] firewalldb: Privacy Mapper schemas, queries and CRUD #1043

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion db/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const (
// daemon.
//
// NOTE: This MUST be updated when a new migration is added.
LatestMigrationVersion = 3
LatestMigrationVersion = 4
)

// MigrationTarget is a functional option that can be passed to applyMigrations
Expand Down
4 changes: 4 additions & 0 deletions db/sqlc/migrations/000004_privacy_pairs.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
DROP INDEX IF EXISTS privacy_pairs_group_id_idx;
DROP INDEX IF EXISTS privacy_pairs_unique_real;
DROP INDEX IF EXISTS privacy_pairs_unique_pseudo;
DROP TABLE IF EXISTS privacy_pairs;
23 changes: 23 additions & 0 deletions db/sqlc/migrations/000004_privacy_pairs.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
-- privacy_pairs stores the privacy map pairs for a given session group.
CREATE TABLE IF NOT EXISTS privacy_pairs (
-- The group ID of the session that this privacy pair is associated
-- with.
group_id BIGINT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this enforce enough or should we use sessions(group_id) here, since we could use a second session's id in a group which would not be allowed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you cannot reference a field that is not the primary key unless it is unique.

We will just need to force this on the CRUD layer

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I figured that, would have been great to enforce this constraint on the db layer, but ok to do it on the CRUD layer, if it's not easily possible


-- The real value of the privacy pair.
real_val TEXT NOT NULL,

-- The pseudo value of the privacy pair.
pseudo_val TEXT NOT NULL
);

-- There should be no duplicate real values for a given group ID.
CREATE UNIQUE INDEX privacy_pairs_unique_real ON privacy_pairs (
group_id, real_val
);

-- There should be no duplicate pseudo values for a given group ID.
CREATE UNIQUE INDEX privacy_pairs_unique_pseudo ON privacy_pairs (
group_id, pseudo_val
);

6 changes: 6 additions & 0 deletions db/sqlc/models.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

96 changes: 96 additions & 0 deletions db/sqlc/privacy_paris.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions db/sqlc/querier.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions db/sqlc/queries/privacy_paris.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
-- name: InsertPrivacyPair :exec
INSERT INTO privacy_pairs (group_id, real_val, pseudo_val)
VALUES ($1, $2, $3);

-- name: GetRealForPseudo :one
SELECT real_val
FROM privacy_pairs
WHERE group_id = $1 AND pseudo_val = $2;

-- name: GetPseudoForReal :one
SELECT pseudo_val
FROM privacy_pairs
WHERE group_id = $1 AND real_val = $2;

-- name: GetAllPrivacyPairs :many
SELECT real_val, pseudo_val
FROM privacy_pairs
WHERE group_id = $1;
10 changes: 5 additions & 5 deletions firewall/privacy_mapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,19 @@ 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
db firewalldb.PrivacyMapper
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,
func NewPrivacyMapper(newDB firewalldb.PrivacyMapper,
randIntn func(int) (int, error),
sessionDB firewalldb.SessionDB) *PrivacyMapper {

return &PrivacyMapper{
newDB: newDB,
db: newDB,
randIntn: randIntn,
sessionDB: sessionDB,
}
Expand Down Expand Up @@ -195,7 +195,7 @@ func (p *PrivacyMapper) checkAndReplaceIncomingRequest(ctx context.Context,
return nil, err
}

db := p.newDB(session.GroupID)
db := p.db.PrivacyDB(session.GroupID)

// If we don't have a handler for the URI, we don't allow the request
// to go through.
Expand Down Expand Up @@ -225,7 +225,7 @@ func (p *PrivacyMapper) replaceOutgoingResponse(ctx context.Context, uri string,
return nil, err
}

db := p.newDB(session.GroupID)
db := p.db.PrivacyDB(session.GroupID)

// If we don't have a handler for the URI, we don't allow the response
// to go to avoid accidental leaks.
Expand Down
12 changes: 6 additions & 6 deletions firewall/privacy_mapper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -902,7 +902,7 @@ func TestPrivacyMapper(t *testing.T) {

// randIntn is used for deterministic testing.
randIntn := func(n int) (int, error) { return 100, nil }
p := NewPrivacyMapper(db.NewSessionDB, randIntn, pd)
p := NewPrivacyMapper(db, randIntn, pd)

rawMsg, err := proto.Marshal(test.msg)
require.NoError(t, err)
Expand Down Expand Up @@ -978,7 +978,7 @@ func TestPrivacyMapper(t *testing.T) {
rawMsg, err := proto.Marshal(msg)
require.NoError(t, err)

p := NewPrivacyMapper(db.NewSessionDB, CryptoRandIntn, pd)
p := NewPrivacyMapper(db, CryptoRandIntn, pd)
require.NoError(t, err)

// We test the independent outgoing amount (incoming amount
Expand Down Expand Up @@ -1071,7 +1071,7 @@ func newMockDB(t *testing.T, preloadRealToPseudo map[string]string,
sessID session.ID) mockDB {

db := mockDB{privDB: make(map[string]*mockPrivacyMapDB)}
sessDB := db.NewSessionDB(sessID)
sessDB := db.PrivacyDB(sessID)

_ = sessDB.Update(context.Background(), func(ctx context.Context,
tx firewalldb.PrivacyMapTx) error {
Expand All @@ -1085,14 +1085,14 @@ func newMockDB(t *testing.T, preloadRealToPseudo map[string]string,
return db
}

func (m mockDB) NewSessionDB(sessionID session.ID) firewalldb.PrivacyMapDB {
db, ok := m.privDB[string(sessionID[:])]
func (m mockDB) PrivacyDB(groupID session.ID) firewalldb.PrivacyMapDB {
db, ok := m.privDB[string(groupID[:])]
if ok {
return db
}

newDB := newMockPrivacyMapDB()
m.privDB[string(sessionID[:])] = newDB
m.privDB[string(groupID[:])] = newDB

return newDB
}
Expand Down
8 changes: 4 additions & 4 deletions firewall/rule_enforcer.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ type RuleEnforcer struct {
actionsDB firewalldb.ActionReadDBGetter
sessionDB firewalldb.SessionDB
markActionErrored func(reqID uint64, reason string) error
newPrivMap firewalldb.NewPrivacyMapDB
privMapDB firewalldb.PrivacyMapper

permsMgr *perms.Manager
getFeaturePerms featurePerms
Expand Down Expand Up @@ -64,7 +64,7 @@ func NewRuleEnforcer(ruleDB firewalldb.RulesDB,
lndClient lndclient.LightningClient, lndConnID string,
ruleMgrs rules.ManagerSet,
markActionErrored func(reqID uint64, reason string) error,
privMap firewalldb.NewPrivacyMapDB) *RuleEnforcer {
privMap firewalldb.PrivacyMapper) *RuleEnforcer {

return &RuleEnforcer{
ruleDB: ruleDB,
Expand All @@ -76,7 +76,7 @@ func NewRuleEnforcer(ruleDB firewalldb.RulesDB,
lndClient: lndClient,
ruleMgrs: ruleMgrs,
markActionErrored: markActionErrored,
newPrivMap: privMap,
privMapDB: privMap,
sessionDB: sessionIDIndex,
lndConnID: lndConnID,
}
Expand Down Expand Up @@ -392,7 +392,7 @@ func (r *RuleEnforcer) initRule(ctx context.Context, reqID uint64, name string,
}

if privacy {
privMap := r.newPrivMap(session.GroupID)
privMap := r.privMapDB.PrivacyDB(session.GroupID)

ruleValues, err = ruleValues.PseudoToReal(
ctx, privMap, session.PrivacyFlags,
Expand Down
15 changes: 11 additions & 4 deletions firewalldb/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,28 @@ var (
ErrNoSuchKeyFound = fmt.Errorf("no such key found")
)

// firewallDBs is an interface that groups the RulesDB and PrivacyMapper
// interfaces.
type firewallDBs interface {
RulesDB
PrivacyMapper
}

// DB manages the firewall rules database.
type DB struct {
started sync.Once
stopped sync.Once

RulesDB
firewallDBs

cancel fn.Option[context.CancelFunc]
}

// NewDB creates a new firewall database. For now, it only contains the
// underlying rules' database.
func NewDB(kvdb RulesDB) *DB {
// underlying rules' and privacy mapper databases.
func NewDB(dbs firewallDBs) *DB {
return &DB{
RulesDB: kvdb,
firewallDBs: dbs,
}
}

Expand Down
8 changes: 8 additions & 0 deletions firewalldb/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,11 @@ type RulesDB interface {
// DeleteTempKVStores deletes all temporary kv stores.
DeleteTempKVStores(ctx context.Context) error
}

// PrivacyMapper is an interface that abstracts access to the privacy mapper
// database.
type PrivacyMapper interface {
// PrivacyDB constructs a PrivacyMapDB that will be indexed under the
// given group ID key.
PrivacyDB(groupID session.ID) PrivacyMapDB
}
6 changes: 0 additions & 6 deletions firewalldb/privacy_mapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import (
"strconv"
"strings"
"sync"

"github.com/lightninglabs/lightning-terminal/session"
)

var (
Expand All @@ -29,10 +27,6 @@ var (
"value already exists")
)

// NewPrivacyMapDB is a function type that takes a group ID and uses it to
// construct a new PrivacyMapDB.
type NewPrivacyMapDB func(groupID session.ID) PrivacyMapDB

// PrivacyMapDB provides an Update and View method that will allow the caller
// to perform atomic read and write transactions defined by PrivacyMapTx on the
// underlying DB.
Expand Down
2 changes: 2 additions & 0 deletions firewalldb/privacy_mapper_kvdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ var (

// PrivacyDB constructs a PrivacyMapDB that will be indexed under the given
// group ID key.
//
// NOTE: this is part of the PrivacyMapper interface.
func (db *BoltDB) PrivacyDB(groupID session.ID) PrivacyMapDB {
return &kvdbExecutor[PrivacyMapTx]{
db: db.DB,
Expand Down
Loading
Loading