mirror of
https://github.com/netbirdio/netbird.git
synced 2025-04-23 02:48:50 +02:00
Fix/user deletion (#1157)
Extend the deleted user info with the username - Because initially, we did not store the user name in the activity db Sometimes, we can not provide the user name in the API response. Fix service user deletion - In case of service user deletion, do not invoke the IdP delete function - Prevent self deletion
This commit is contained in:
parent
e260270825
commit
da7b6b11ad
@ -18,7 +18,9 @@ type Event struct {
|
|||||||
ID uint64
|
ID uint64
|
||||||
// InitiatorID is the ID of an object that initiated the event (e.g., a user)
|
// InitiatorID is the ID of an object that initiated the event (e.g., a user)
|
||||||
InitiatorID string
|
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
|
InitiatorEmail string
|
||||||
// TargetID is the ID of an object that was effected by the event (e.g., a peer)
|
// TargetID is the ID of an object that was effected by the event (e.g., a peer)
|
||||||
TargetID string
|
TargetID string
|
||||||
@ -42,6 +44,7 @@ func (e *Event) Copy() *Event {
|
|||||||
Activity: e.Activity,
|
Activity: e.Activity,
|
||||||
ID: e.ID,
|
ID: e.ID,
|
||||||
InitiatorID: e.InitiatorID,
|
InitiatorID: e.InitiatorID,
|
||||||
|
InitiatorName: e.InitiatorName,
|
||||||
InitiatorEmail: e.InitiatorEmail,
|
InitiatorEmail: e.InitiatorEmail,
|
||||||
TargetID: e.TargetID,
|
TargetID: e.TargetID,
|
||||||
AccountID: e.AccountID,
|
AccountID: e.AccountID,
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
|
|
||||||
var iv = []byte{10, 22, 13, 79, 05, 8, 52, 91, 87, 98, 88, 98, 35, 25, 13, 05}
|
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
|
block cipher.Block
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ func GenerateKey() (string, error) {
|
|||||||
return readableKey, nil
|
return readableKey, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEmailEncrypt(key string) (*EmailEncrypt, error) {
|
func NewFieldEncrypt(key string) (*FieldEncrypt, error) {
|
||||||
binKey, err := base64.StdEncoding.DecodeString(key)
|
binKey, err := base64.StdEncoding.DecodeString(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -35,14 +35,14 @@ func NewEmailEncrypt(key string) (*EmailEncrypt, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
ec := &EmailEncrypt{
|
ec := &FieldEncrypt{
|
||||||
block: block,
|
block: block,
|
||||||
}
|
}
|
||||||
|
|
||||||
return ec, nil
|
return ec, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *EmailEncrypt) Encrypt(payload string) string {
|
func (ec *FieldEncrypt) Encrypt(payload string) string {
|
||||||
plainText := pkcs5Padding([]byte(payload))
|
plainText := pkcs5Padding([]byte(payload))
|
||||||
cipherText := make([]byte, len(plainText))
|
cipherText := make([]byte, len(plainText))
|
||||||
cbc := cipher.NewCBCEncrypter(ec.block, iv)
|
cbc := cipher.NewCBCEncrypter(ec.block, iv)
|
||||||
@ -50,7 +50,7 @@ func (ec *EmailEncrypt) Encrypt(payload string) string {
|
|||||||
return base64.StdEncoding.EncodeToString(cipherText)
|
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)
|
cipherText, err := base64.StdEncoding.DecodeString(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -10,7 +10,7 @@ func TestGenerateKey(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to generate key: %s", err)
|
t.Fatalf("failed to generate key: %s", err)
|
||||||
}
|
}
|
||||||
ee, err := NewEmailEncrypt(key)
|
ee, err := NewFieldEncrypt(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to init email encryption: %s", err)
|
t.Fatalf("failed to init email encryption: %s", err)
|
||||||
}
|
}
|
||||||
@ -36,7 +36,7 @@ func TestCorruptKey(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to generate key: %s", err)
|
t.Fatalf("failed to generate key: %s", err)
|
||||||
}
|
}
|
||||||
ee, err := NewEmailEncrypt(key)
|
ee, err := NewFieldEncrypt(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to init email encryption: %s", err)
|
t.Fatalf("failed to init email encryption: %s", err)
|
||||||
}
|
}
|
||||||
@ -51,13 +51,13 @@ func TestCorruptKey(t *testing.T) {
|
|||||||
t.Fatalf("failed to generate key: %s", err)
|
t.Fatalf("failed to generate key: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ee, err = NewEmailEncrypt(newKey)
|
ee, err = NewFieldEncrypt(newKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to init email encryption: %s", err)
|
t.Fatalf("failed to init email encryption: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := ee.Decrypt(encrypted)
|
res, _ := ee.Decrypt(encrypted)
|
||||||
if err == nil || res == testData {
|
if res == testData {
|
||||||
t.Fatalf("incorrect decryption, the result is: %s", res)
|
t.Fatalf("incorrect decryption, the result is: %s", res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
_ "github.com/mattn/go-sqlite3" // sqlite driver
|
_ "github.com/mattn/go-sqlite3"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/management/server/activity"
|
"github.com/netbirdio/netbird/management/server/activity"
|
||||||
@ -25,16 +25,16 @@ const (
|
|||||||
"meta TEXT," +
|
"meta TEXT," +
|
||||||
" target_id 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
|
FROM events
|
||||||
LEFT JOIN deleted_users i ON events.initiator_id = i.id
|
LEFT JOIN deleted_users i ON events.initiator_id = i.id
|
||||||
LEFT JOIN deleted_users t ON events.target_id = t.id
|
LEFT JOIN deleted_users t ON events.target_id = t.id
|
||||||
WHERE account_id = ?
|
WHERE account_id = ?
|
||||||
ORDER BY timestamp DESC LIMIT ? OFFSET ?;`
|
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
|
FROM events
|
||||||
LEFT JOIN deleted_users i ON events.initiator_id = i.id
|
LEFT JOIN deleted_users i ON events.initiator_id = i.id
|
||||||
LEFT JOIN deleted_users t ON events.target_id = t.id
|
LEFT JOIN deleted_users t ON events.target_id = t.id
|
||||||
@ -44,13 +44,13 @@ const (
|
|||||||
insertQuery = "INSERT INTO events(activity, timestamp, initiator_id, target_id, account_id, meta) " +
|
insertQuery = "INSERT INTO events(activity, timestamp, initiator_id, target_id, account_id, meta) " +
|
||||||
"VALUES(?, ?, ?, ?, ?, ?)"
|
"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
|
// Store is the implementation of the activity.Store interface backed by SQLite
|
||||||
type Store struct {
|
type Store struct {
|
||||||
db *sql.DB
|
db *sql.DB
|
||||||
emailEncrypt *EmailEncrypt
|
fieldEncrypt *FieldEncrypt
|
||||||
|
|
||||||
insertStatement *sql.Stmt
|
insertStatement *sql.Stmt
|
||||||
selectAscStatement *sql.Stmt
|
selectAscStatement *sql.Stmt
|
||||||
@ -66,49 +66,63 @@ func NewSQLiteStore(dataDir string, encryptionKey string) (*Store, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
crypt, err := NewEmailEncrypt(encryptionKey)
|
crypt, err := NewFieldEncrypt(encryptionKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
_ = db.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = db.Exec(createTableQuery)
|
_, err = db.Exec(createTableQuery)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
_ = db.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = db.Exec(creatTableAccountEmailQuery)
|
_, err = db.Exec(creatTableDeletedUsersQuery)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
_ = db.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = updateDeletedUsersTable(db)
|
||||||
|
if err != nil {
|
||||||
|
_ = db.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
insertStmt, err := db.Prepare(insertQuery)
|
insertStmt, err := db.Prepare(insertQuery)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
_ = db.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
selectDescStmt, err := db.Prepare(selectDescQuery)
|
selectDescStmt, err := db.Prepare(selectDescQuery)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
_ = db.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
selectAscStmt, err := db.Prepare(selectAscQuery)
|
selectAscStmt, err := db.Prepare(selectAscQuery)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
_ = db.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteUserStmt, err := db.Prepare(insertDeleteUserQuery)
|
deleteUserStmt, err := db.Prepare(insertDeleteUserQuery)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
_ = db.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
s := &Store{
|
s := &Store{
|
||||||
db: db,
|
db: db,
|
||||||
emailEncrypt: crypt,
|
fieldEncrypt: crypt,
|
||||||
insertStatement: insertStmt,
|
insertStatement: insertStmt,
|
||||||
selectDescStatement: selectDescStmt,
|
selectDescStatement: selectDescStmt,
|
||||||
selectAscStatement: selectAscStmt,
|
selectAscStatement: selectAscStmt,
|
||||||
deleteUserStmt: deleteUserStmt,
|
deleteUserStmt: deleteUserStmt,
|
||||||
}
|
}
|
||||||
|
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,12 +133,14 @@ func (store *Store) processResult(result *sql.Rows) ([]*activity.Event, error) {
|
|||||||
var operation activity.Activity
|
var operation activity.Activity
|
||||||
var timestamp time.Time
|
var timestamp time.Time
|
||||||
var initiator string
|
var initiator string
|
||||||
|
var initiatorName *string
|
||||||
var initiatorEmail *string
|
var initiatorEmail *string
|
||||||
var target string
|
var target string
|
||||||
|
var targetUserName *string
|
||||||
var targetEmail *string
|
var targetEmail *string
|
||||||
var account string
|
var account string
|
||||||
var jsonMeta string
|
var jsonMeta string
|
||||||
err := result.Scan(&id, &operation, ×tamp, &initiator, &initiatorEmail, &target, &targetEmail, &account, &jsonMeta)
|
err := result.Scan(&id, &operation, ×tamp, &initiator, &initiatorName, &initiatorEmail, &target, &targetUserName, &targetEmail, &account, &jsonMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -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 {
|
if targetEmail != nil {
|
||||||
email, err := store.emailEncrypt.Decrypt(*targetEmail)
|
email, err := store.fieldEncrypt.Decrypt(*targetEmail)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to decrypt email address for target id: %s", target)
|
log.Errorf("failed to decrypt email address for target id: %s", target)
|
||||||
meta["email"] = ""
|
meta["email"] = ""
|
||||||
@ -157,8 +183,17 @@ func (store *Store) processResult(result *sql.Rows) ([]*activity.Event, error) {
|
|||||||
Meta: meta,
|
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 {
|
if initiatorEmail != nil {
|
||||||
email, err := store.emailEncrypt.Decrypt(*initiatorEmail)
|
email, err := store.fieldEncrypt.Decrypt(*initiatorEmail)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to decrypt email address of initiator: %s", initiator)
|
log.Errorf("failed to decrypt email address of initiator: %s", initiator)
|
||||||
} else {
|
} else {
|
||||||
@ -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
|
// 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) {
|
func (store *Store) Save(event *activity.Event) (*activity.Event, error) {
|
||||||
var jsonMeta string
|
var jsonMeta string
|
||||||
meta, err := store.saveDeletedUserEmailInEncrypted(event)
|
meta, err := store.saveDeletedUserEmailAndNameInEncrypted(event)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -219,26 +254,31 @@ func (store *Store) Save(event *activity.Event) (*activity.Event, error) {
|
|||||||
return eventCopy, nil
|
return eventCopy, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// saveDeletedUserEmailInEncrypted if the meta contains email then store it in encrypted way and delete this item from
|
// saveDeletedUserEmailAndNameInEncrypted if the meta contains email and name then store it in encrypted way and delete
|
||||||
// meta map
|
// this item from meta map
|
||||||
func (store *Store) saveDeletedUserEmailInEncrypted(event *activity.Event) (map[string]any, error) {
|
func (store *Store) saveDeletedUserEmailAndNameInEncrypted(event *activity.Event) (map[string]any, error) {
|
||||||
email, ok := event.Meta["email"]
|
email, ok := event.Meta["email"]
|
||||||
if !ok {
|
if !ok {
|
||||||
return event.Meta, nil
|
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))
|
encryptedEmail := store.fieldEncrypt.Encrypt(fmt.Sprintf("%s", email))
|
||||||
_, err := store.deleteUserStmt.Exec(event.TargetID, encrypted)
|
encryptedName := store.fieldEncrypt.Encrypt(fmt.Sprintf("%s", name))
|
||||||
|
_, err := store.deleteUserStmt.Exec(event.TargetID, encryptedEmail, encryptedName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(event.Meta) == 1 {
|
if len(event.Meta) == 2 {
|
||||||
return nil, nil // nolint
|
return nil, nil // nolint
|
||||||
}
|
}
|
||||||
delete(event.Meta, "email")
|
delete(event.Meta, "email")
|
||||||
|
delete(event.Meta, "name")
|
||||||
return event.Meta, nil
|
return event.Meta, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,3 +289,44 @@ func (store *Store) Close() error {
|
|||||||
}
|
}
|
||||||
return nil
|
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, ¬Null, &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
|
||||||
|
}
|
||||||
|
@ -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.
|
description: The ID of the initiator of the event. E.g., an ID of a user that triggered the event.
|
||||||
type: string
|
type: string
|
||||||
example: google-oauth2|123456789012345678901
|
example: google-oauth2|123456789012345678901
|
||||||
|
initiator_name:
|
||||||
|
description: The name of the initiator of the event.
|
||||||
|
type: string
|
||||||
|
example: John Doe
|
||||||
initiator_email:
|
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.
|
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
|
type: string
|
||||||
@ -942,6 +946,7 @@ components:
|
|||||||
- activity
|
- activity
|
||||||
- activity_code
|
- activity_code
|
||||||
- initiator_id
|
- initiator_id
|
||||||
|
- initiator_name
|
||||||
- initiator_email
|
- initiator_email
|
||||||
- target_id
|
- target_id
|
||||||
- meta
|
- meta
|
||||||
|
@ -170,6 +170,9 @@ type Event struct {
|
|||||||
// InitiatorId The ID of the initiator of the event. E.g., an ID of a user that triggered the event.
|
// InitiatorId The ID of the initiator of the event. E.g., an ID of a user that triggered the event.
|
||||||
InitiatorId string `json:"initiator_id"`
|
InitiatorId string `json:"initiator_id"`
|
||||||
|
|
||||||
|
// InitiatorName The name of the initiator of the event.
|
||||||
|
InitiatorName string `json:"initiator_name"`
|
||||||
|
|
||||||
// Meta The metadata of the event
|
// Meta The metadata of the event
|
||||||
Meta map[string]string `json:"meta"`
|
Meta map[string]string `json:"meta"`
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ func (h *EventsHandler) GetAllEvents(w http.ResponseWriter, r *http.Request) {
|
|||||||
events[i] = toEventResponse(e)
|
events[i] = toEventResponse(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = h.fillEventsWithInitiatorEmail(events, account.Id, user.Id)
|
err = h.fillEventsWithUserInfo(events, account.Id, user.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.WriteError(err, w)
|
util.WriteError(err, w)
|
||||||
return
|
return
|
||||||
@ -59,8 +59,8 @@ func (h *EventsHandler) GetAllEvents(w http.ResponseWriter, r *http.Request) {
|
|||||||
util.WriteJSONObject(w, events)
|
util.WriteJSONObject(w, events)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *EventsHandler) fillEventsWithInitiatorEmail(events []*api.Event, accountId, userId string) error {
|
func (h *EventsHandler) fillEventsWithUserInfo(events []*api.Event, accountId, userId string) error {
|
||||||
// build email map based on users
|
// build email, name maps based on users
|
||||||
userInfos, err := h.accountManager.GetUsersFromAccount(accountId, userId)
|
userInfos, err := h.accountManager.GetUsersFromAccount(accountId, userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to get users from account: %s", err)
|
log.Errorf("failed to get users from account: %s", err)
|
||||||
@ -68,19 +68,39 @@ func (h *EventsHandler) fillEventsWithInitiatorEmail(events []*api.Event, accoun
|
|||||||
}
|
}
|
||||||
|
|
||||||
emails := make(map[string]string)
|
emails := make(map[string]string)
|
||||||
|
names := make(map[string]string)
|
||||||
for _, ui := range userInfos {
|
for _, ui := range userInfos {
|
||||||
emails[ui.ID] = ui.Email
|
emails[ui.ID] = ui.Email
|
||||||
|
names[ui.ID] = ui.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
// fill event with email of initiator
|
|
||||||
var ok bool
|
var ok bool
|
||||||
for _, event := range events {
|
for _, event := range events {
|
||||||
|
// fill initiator
|
||||||
if event.InitiatorEmail == "" {
|
if event.InitiatorEmail == "" {
|
||||||
event.InitiatorEmail, ok = emails[event.InitiatorId]
|
event.InitiatorEmail, ok = emails[event.InitiatorId]
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Warnf("failed to resolve email for initiator: %s", event.InitiatorId)
|
log.Warnf("failed to resolve email for initiator: %s", event.InitiatorId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if event.InitiatorName == "" {
|
||||||
|
// here to allowed to be empty because in the first release we did not store the name
|
||||||
|
event.InitiatorName = names[event.InitiatorId]
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill target meta
|
||||||
|
email, ok := emails[event.TargetId]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
event.Meta["email"] = email
|
||||||
|
|
||||||
|
username, ok := names[event.TargetId]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
event.Meta["username"] = username
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -95,6 +115,7 @@ func toEventResponse(event *activity.Event) *api.Event {
|
|||||||
e := &api.Event{
|
e := &api.Event{
|
||||||
Id: fmt.Sprint(event.ID),
|
Id: fmt.Sprint(event.ID),
|
||||||
InitiatorId: event.InitiatorID,
|
InitiatorId: event.InitiatorID,
|
||||||
|
InitiatorName: event.InitiatorName,
|
||||||
InitiatorEmail: event.InitiatorEmail,
|
InitiatorEmail: event.InitiatorEmail,
|
||||||
Activity: event.Activity.Message(),
|
Activity: event.Activity.Message(),
|
||||||
ActivityCode: api.EventActivityCode(event.Activity.StringCode()),
|
ActivityCode: api.EventActivityCode(event.Activity.StringCode()),
|
||||||
|
@ -309,6 +309,9 @@ func (am *DefaultAccountManager) GetUser(claims jwtclaims.AuthorizationClaims) (
|
|||||||
|
|
||||||
// DeleteUser deletes a user from the given account.
|
// DeleteUser deletes a user from the given account.
|
||||||
func (am *DefaultAccountManager) DeleteUser(accountID, initiatorUserID string, targetUserID string) error {
|
func (am *DefaultAccountManager) DeleteUser(accountID, initiatorUserID string, targetUserID string) error {
|
||||||
|
if initiatorUserID == targetUserID {
|
||||||
|
return status.Errorf(status.InvalidArgument, "self deletion is not allowed")
|
||||||
|
}
|
||||||
unlock := am.Store.AcquireAccountLock(accountID)
|
unlock := am.Store.AcquireAccountLock(accountID)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
|
|
||||||
@ -340,7 +343,7 @@ func (am *DefaultAccountManager) DeleteUser(accountID, initiatorUserID string, t
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
targetUserEmail, err := am.getEmailOfTargetUser(account.Id, initiatorUserID, targetUserID)
|
tuEmail, tuName, err := am.getEmailAndNameOfTargetUser(account.Id, initiatorUserID, targetUserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to resolve email address: %s", err)
|
log.Errorf("failed to resolve email address: %s", err)
|
||||||
return err
|
return err
|
||||||
@ -352,15 +355,15 @@ func (am *DefaultAccountManager) DeleteUser(accountID, initiatorUserID string, t
|
|||||||
meta = map[string]any{"name": targetUser.ServiceUserName}
|
meta = map[string]any{"name": targetUser.ServiceUserName}
|
||||||
eventAction = activity.ServiceUserDeleted
|
eventAction = activity.ServiceUserDeleted
|
||||||
} else {
|
} else {
|
||||||
meta = map[string]any{"email": targetUserEmail}
|
meta = map[string]any{"name": tuName, "email": tuEmail}
|
||||||
eventAction = activity.UserDeleted
|
eventAction = activity.UserDeleted
|
||||||
|
|
||||||
}
|
}
|
||||||
am.storeEvent(initiatorUserID, targetUserID, accountID, eventAction, meta)
|
am.storeEvent(initiatorUserID, targetUserID, accountID, eventAction, meta)
|
||||||
|
|
||||||
if !isNil(am.idpManager) {
|
if !targetUser.IsServiceUser && !isNil(am.idpManager) {
|
||||||
err := am.deleteUserFromIDP(targetUserID, accountID)
|
err := am.deleteUserFromIDP(targetUserID, accountID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Debugf("failed to delete user from IDP: %s", targetUserID)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -876,18 +879,18 @@ func (am *DefaultAccountManager) deleteUserFromIDP(targetUserID, accountID strin
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (am *DefaultAccountManager) getEmailOfTargetUser(accountId string, initiatorId, targetId string) (string, error) {
|
func (am *DefaultAccountManager) getEmailAndNameOfTargetUser(accountId, initiatorId, targetId string) (string, string, error) {
|
||||||
userInfos, err := am.GetUsersFromAccount(accountId, initiatorId)
|
userInfos, err := am.GetUsersFromAccount(accountId, initiatorId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
for _, ui := range userInfos {
|
for _, ui := range userInfos {
|
||||||
if ui.ID == targetId {
|
if ui.ID == targetId {
|
||||||
return ui.Email, nil
|
return ui.Email, ui.Name, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", fmt.Errorf("email not found for user: %s", targetId)
|
return "", "", fmt.Errorf("user info not found for user: %s", targetId)
|
||||||
}
|
}
|
||||||
|
|
||||||
func findUserInIDPUserdata(userID string, userData []*idp.UserData) (*idp.UserData, bool) {
|
func findUserInIDPUserdata(userID string, userData []*idp.UserData) (*idp.UserData, bool) {
|
||||||
|
@ -424,7 +424,7 @@ func TestUser_DeleteUser_ServiceUser(t *testing.T) {
|
|||||||
assert.Nil(t, store.Accounts[mockAccountID].Users[mockServiceUserID])
|
assert.Nil(t, store.Accounts[mockAccountID].Users[mockServiceUserID])
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUser_DeleteUser_regularUser(t *testing.T) {
|
func TestUser_DeleteUser_SelfDelete(t *testing.T) {
|
||||||
store := newStore(t)
|
store := newStore(t)
|
||||||
account := newAccountWithId(mockAccountID, mockUserID, "")
|
account := newAccountWithId(mockAccountID, mockUserID, "")
|
||||||
|
|
||||||
@ -439,6 +439,32 @@ func TestUser_DeleteUser_regularUser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err = am.DeleteUser(mockAccountID, mockUserID, mockUserID)
|
err = am.DeleteUser(mockAccountID, mockUserID, mockUserID)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("failed to prevent self deletion")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUser_DeleteUser_regularUser(t *testing.T) {
|
||||||
|
store := newStore(t)
|
||||||
|
account := newAccountWithId(mockAccountID, mockUserID, "")
|
||||||
|
targetId := "user2"
|
||||||
|
account.Users[targetId] = &User{
|
||||||
|
Id: targetId,
|
||||||
|
IsServiceUser: true,
|
||||||
|
ServiceUserName: "user2username",
|
||||||
|
}
|
||||||
|
|
||||||
|
err := store.SaveAccount(account)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error when saving account: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
am := DefaultAccountManager{
|
||||||
|
Store: store,
|
||||||
|
eventStore: &activity.InMemoryEventStore{},
|
||||||
|
}
|
||||||
|
|
||||||
|
err = am.DeleteUser(mockAccountID, mockUserID, targetId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error: %s", err)
|
t.Errorf("unexpected error: %s", err)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user