Skip to content

Commit

Permalink
Store in encrypted the username
Browse files Browse the repository at this point in the history
  • Loading branch information
pappz committed Sep 21, 2023
1 parent 132616d commit 59ade98
Show file tree
Hide file tree
Showing 8 changed files with 139 additions and 39 deletions.
5 changes: 4 additions & 1 deletion management/server/activity/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ type Event struct {
ID uint64
// InitiatorID is the ID of an object that initiated the event (e.g., a user)
InitiatorID string
// InitiatorEmail is the email address of an object that initiated the event. This will be set on deleted users only
// InitiatorName is the name of an object that initiated the event.
InitiatorName string
// InitiatorEmail is the email address of an object that initiated the event.
InitiatorEmail string
// TargetID is the ID of an object that was effected by the event (e.g., a peer)
TargetID string
Expand All @@ -42,6 +44,7 @@ func (e *Event) Copy() *Event {
Activity: e.Activity,
ID: e.ID,
InitiatorID: e.InitiatorID,
InitiatorName: e.InitiatorName,
InitiatorEmail: e.InitiatorEmail,
TargetID: e.TargetID,
AccountID: e.AccountID,
Expand Down
10 changes: 5 additions & 5 deletions management/server/activity/sqlite/crypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (

var iv = []byte{10, 22, 13, 79, 05, 8, 52, 91, 87, 98, 88, 98, 35, 25, 13, 05}

type EmailEncrypt struct {
type FieldEncrypt struct {
block cipher.Block
}

Expand All @@ -25,7 +25,7 @@ func GenerateKey() (string, error) {
return readableKey, nil
}

func NewEmailEncrypt(key string) (*EmailEncrypt, error) {
func NewFieldEncrypt(key string) (*FieldEncrypt, error) {
binKey, err := base64.StdEncoding.DecodeString(key)
if err != nil {
return nil, err
Expand All @@ -35,22 +35,22 @@ func NewEmailEncrypt(key string) (*EmailEncrypt, error) {
if err != nil {
return nil, err
}
ec := &EmailEncrypt{
ec := &FieldEncrypt{
block: block,
}

return ec, nil
}

func (ec *EmailEncrypt) Encrypt(payload string) string {
func (ec *FieldEncrypt) Encrypt(payload string) string {
plainText := pkcs5Padding([]byte(payload))
cipherText := make([]byte, len(plainText))
cbc := cipher.NewCBCEncrypter(ec.block, iv)
cbc.CryptBlocks(cipherText, plainText)
return base64.StdEncoding.EncodeToString(cipherText)
}

func (ec *EmailEncrypt) Decrypt(data string) (string, error) {
func (ec *FieldEncrypt) Decrypt(data string) (string, error) {
cipherText, err := base64.StdEncoding.DecodeString(data)
if err != nil {
return "", err
Expand Down
6 changes: 3 additions & 3 deletions management/server/activity/sqlite/crypt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ func TestGenerateKey(t *testing.T) {
if err != nil {
t.Fatalf("failed to generate key: %s", err)
}
ee, err := NewEmailEncrypt(key)
ee, err := NewFieldEncrypt(key)
if err != nil {
t.Fatalf("failed to init email encryption: %s", err)
}
Expand All @@ -36,7 +36,7 @@ func TestCorruptKey(t *testing.T) {
if err != nil {
t.Fatalf("failed to generate key: %s", err)
}
ee, err := NewEmailEncrypt(key)
ee, err := NewFieldEncrypt(key)
if err != nil {
t.Fatalf("failed to init email encryption: %s", err)
}
Expand All @@ -51,7 +51,7 @@ func TestCorruptKey(t *testing.T) {
t.Fatalf("failed to generate key: %s", err)
}

ee, err = NewEmailEncrypt(newKey)
ee, err = NewFieldEncrypt(newKey)
if err != nil {
t.Fatalf("failed to init email encryption: %s", err)
}
Expand Down
121 changes: 101 additions & 20 deletions management/server/activity/sqlite/sqlite.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"path/filepath"
"time"

_ "github.com/mattn/go-sqlite3" // sqlite driver
_ "github.com/mattn/go-sqlite3"
log "github.com/sirupsen/logrus"

"github.com/netbirdio/netbird/management/server/activity"
Expand All @@ -25,16 +25,16 @@ const (
"meta TEXT," +
" target_id TEXT);"

creatTableAccountEmailQuery = `CREATE TABLE IF NOT EXISTS deleted_users (id TEXT NOT NULL, email TEXT NOT NULL);`
creatTableDeletedUsersQuery = `CREATE TABLE IF NOT EXISTS deleted_users (id TEXT NOT NULL, email TEXT NOT NULL, name TEXT);`

selectDescQuery = `SELECT events.id, activity, timestamp, initiator_id, i.email as "initiator_email", target_id, t.email as "target_email", account_id, meta
selectDescQuery = `SELECT events.id, activity, timestamp, initiator_id, i.name as "initiator_name", i.email as "initiator_email", target_id, t.name as "target_name", t.email as "target_email", account_id, meta
FROM events
LEFT JOIN deleted_users i ON events.initiator_id = i.id
LEFT JOIN deleted_users t ON events.target_id = t.id
WHERE account_id = ?
ORDER BY timestamp DESC LIMIT ? OFFSET ?;`

selectAscQuery = `SELECT events.id, activity, timestamp, initiator_id, i.email as "initiator_email", target_id, t.email as "target_email", account_id, meta
selectAscQuery = `SELECT events.id, activity, timestamp, initiator_id, i.name as "initiator_name", i.email as "initiator_email", target_id, t.name as "target_name", t.email as "target_email", account_id, meta
FROM events
LEFT JOIN deleted_users i ON events.initiator_id = i.id
LEFT JOIN deleted_users t ON events.target_id = t.id
Expand All @@ -44,13 +44,13 @@ const (
insertQuery = "INSERT INTO events(activity, timestamp, initiator_id, target_id, account_id, meta) " +
"VALUES(?, ?, ?, ?, ?, ?)"

insertDeleteUserQuery = `INSERT INTO deleted_users(id, email) VALUES(?, ?)`
insertDeleteUserQuery = `INSERT INTO deleted_users(id, email, name) VALUES(?, ?, ?)`
)

// Store is the implementation of the activity.Store interface backed by SQLite
type Store struct {
db *sql.DB
emailEncrypt *EmailEncrypt
fieldEncrypt *FieldEncrypt

insertStatement *sql.Stmt
selectAscStatement *sql.Stmt
Expand All @@ -66,49 +66,63 @@ func NewSQLiteStore(dataDir string, encryptionKey string) (*Store, error) {
return nil, err
}

crypt, err := NewEmailEncrypt(encryptionKey)
crypt, err := NewFieldEncrypt(encryptionKey)
if err != nil {
_ = db.Close()
return nil, err
}

_, err = db.Exec(createTableQuery)
if err != nil {
_ = db.Close()
return nil, err
}

_, err = db.Exec(creatTableAccountEmailQuery)
_, err = db.Exec(creatTableDeletedUsersQuery)
if err != nil {
_ = db.Close()
return nil, err
}

err = updateDeletedUsersTable(db)
if err != nil {
_ = db.Close()
return nil, err
}

insertStmt, err := db.Prepare(insertQuery)
if err != nil {
_ = db.Close()
return nil, err
}

selectDescStmt, err := db.Prepare(selectDescQuery)
if err != nil {
_ = db.Close()
return nil, err
}

selectAscStmt, err := db.Prepare(selectAscQuery)
if err != nil {
_ = db.Close()
return nil, err
}

deleteUserStmt, err := db.Prepare(insertDeleteUserQuery)
if err != nil {
_ = db.Close()
return nil, err
}

s := &Store{
db: db,
emailEncrypt: crypt,
fieldEncrypt: crypt,
insertStatement: insertStmt,
selectDescStatement: selectDescStmt,
selectAscStatement: selectAscStmt,
deleteUserStmt: deleteUserStmt,
}

return s, nil
}

Expand All @@ -119,12 +133,14 @@ func (store *Store) processResult(result *sql.Rows) ([]*activity.Event, error) {
var operation activity.Activity
var timestamp time.Time
var initiator string
var initiatorName *string
var initiatorEmail *string
var target string
var targetUserName *string
var targetEmail *string
var account string
var jsonMeta string
err := result.Scan(&id, &operation, &timestamp, &initiator, &initiatorEmail, &target, &targetEmail, &account, &jsonMeta)
err := result.Scan(&id, &operation, &timestamp, &initiator, &initiatorName, &initiatorEmail, &target, &targetUserName, &targetEmail, &account, &jsonMeta)
if err != nil {
return nil, err
}
Expand All @@ -137,8 +153,18 @@ func (store *Store) processResult(result *sql.Rows) ([]*activity.Event, error) {
}
}

if targetUserName != nil {
name, err := store.fieldEncrypt.Decrypt(*targetUserName)
if err != nil {
log.Errorf("failed to decrypt username for target id: %s", target)
meta["userName"] = ""
} else {
meta["userName"] = name
}
}

if targetEmail != nil {
email, err := store.emailEncrypt.Decrypt(*targetEmail)
email, err := store.fieldEncrypt.Decrypt(*targetEmail)
if err != nil {
log.Errorf("failed to decrypt email address for target id: %s", target)
meta["email"] = ""
Expand All @@ -157,8 +183,17 @@ func (store *Store) processResult(result *sql.Rows) ([]*activity.Event, error) {
Meta: meta,
}

if initiatorName != nil {
name, err := store.fieldEncrypt.Decrypt(*initiatorName)
if err != nil {
log.Errorf("failed to decrypt username of initiator: %s", initiator)
} else {
event.InitiatorName = name
}
}

if initiatorEmail != nil {
email, err := store.emailEncrypt.Decrypt(*initiatorEmail)
email, err := store.fieldEncrypt.Decrypt(*initiatorEmail)
if err != nil {
log.Errorf("failed to decrypt email address of initiator: %s", initiator)
} else {
Expand Down Expand Up @@ -191,7 +226,7 @@ func (store *Store) Get(accountID string, offset, limit int, descending bool) ([
// Save an event in the SQLite events table end encrypt the "email" element in meta map
func (store *Store) Save(event *activity.Event) (*activity.Event, error) {
var jsonMeta string
meta, err := store.saveDeletedUserEmailInEncrypted(event)
meta, err := store.saveDeletedUserEmailAndNameInEncrypted(event)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -219,26 +254,31 @@ func (store *Store) Save(event *activity.Event) (*activity.Event, error) {
return eventCopy, nil
}

// saveDeletedUserEmailInEncrypted if the meta contains email then store it in encrypted way and delete this item from
// meta map
func (store *Store) saveDeletedUserEmailInEncrypted(event *activity.Event) (map[string]any, error) {
// saveDeletedUserEmailAndNameInEncrypted if the meta contains email and name then store it in encrypted way and delete
// this item from meta map
func (store *Store) saveDeletedUserEmailAndNameInEncrypted(event *activity.Event) (map[string]any, error) {
email, ok := event.Meta["email"]
if !ok {
return event.Meta, nil
}

delete(event.Meta, "email")
name, ok := event.Meta["name"]
if !ok {
return event.Meta, nil
}

encrypted := store.emailEncrypt.Encrypt(fmt.Sprintf("%s", email))
_, err := store.deleteUserStmt.Exec(event.TargetID, encrypted)
encryptedEmail := store.fieldEncrypt.Encrypt(fmt.Sprintf("%s", email))
encryptedName := store.fieldEncrypt.Encrypt(fmt.Sprintf("%s", name))
_, err := store.deleteUserStmt.Exec(event.TargetID, encryptedEmail, encryptedName)
if err != nil {
return nil, err
}

if len(event.Meta) == 1 {
if len(event.Meta) == 2 {
return nil, nil // nolint
}
delete(event.Meta, "email")
delete(event.Meta, "name")
return event.Meta, nil
}

Expand All @@ -249,3 +289,44 @@ func (store *Store) Close() error {
}
return nil
}

func updateDeletedUsersTable(db *sql.DB) error {
log.Debugf("check deleted_users table version")
rows, err := db.Query(`PRAGMA table_info(deleted_users);`)
if err != nil {
return err
}
defer rows.Close()
found := false
for rows.Next() {
var (
cid int
name string
dataType string
notNull int
dfltVal sql.NullString
pk int
)
err := rows.Scan(&cid, &name, &dataType, &notNull, &dfltVal, &pk)
if err != nil {
return err
}
if name == "name" {
found = true
break
}
}

err = rows.Err()
if err != nil {
return err
}

if found {
return nil
}

log.Debugf("update delted_users table")
_, err = db.Exec(`ALTER TABLE deleted_users ADD COLUMN name TEXT;`)
return err
}
5 changes: 5 additions & 0 deletions management/server/http/api/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -922,6 +922,10 @@ components:
description: The ID of the initiator of the event. E.g., an ID of a user that triggered the event.
type: string
example: google-oauth2|123456789012345678901
initiator_name:
description: The name of the initiator of the event.
type: string
example: John Doe
initiator_email:
description: The e-mail address of the initiator of the event. E.g., an e-mail of a user that triggered the event.
type: string
Expand All @@ -942,6 +946,7 @@ components:
- activity
- activity_code
- initiator_id
- initiator_name
- initiator_email
- target_id
- meta
Expand Down
3 changes: 3 additions & 0 deletions management/server/http/api/types.gen.go

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

Loading

0 comments on commit 59ade98

Please sign in to comment.