mirror of
https://github.com/netbirdio/netbird.git
synced 2026-08-30 19:41:30 +02:00
[management] activity external user info (#3461)
This commit is contained in:
committed by
Pedro Costa
parent
f9f47b0ad8
commit
1763a953f4
@@ -10,6 +10,8 @@ import (
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/activity"
|
||||
"github.com/netbirdio/netbird/management/server/status"
|
||||
"github.com/netbirdio/netbird/management/server/store"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
func isEnabled() bool {
|
||||
@@ -19,16 +21,12 @@ func isEnabled() bool {
|
||||
|
||||
// GetEvents returns a list of activity events of an account
|
||||
func (am *DefaultAccountManager) GetEvents(ctx context.Context, accountID, userID string) ([]*activity.Event, error) {
|
||||
unlock := am.Store.AcquireWriteLockByUID(ctx, accountID)
|
||||
defer unlock()
|
||||
|
||||
account, err := am.Store.GetAccount(ctx, accountID)
|
||||
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user, err := account.FindUser(userID)
|
||||
if err != nil {
|
||||
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -58,6 +56,11 @@ func (am *DefaultAccountManager) GetEvents(ctx context.Context, accountID, userI
|
||||
filtered = append(filtered, event)
|
||||
}
|
||||
|
||||
err = am.fillEventsWithUserInfo(ctx, events, accountID, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return filtered, nil
|
||||
}
|
||||
|
||||
@@ -79,3 +82,145 @@ func (am *DefaultAccountManager) StoreEvent(ctx context.Context, initiatorID, ta
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
type eventUserInfo struct {
|
||||
email string
|
||||
name string
|
||||
accountId string
|
||||
}
|
||||
|
||||
func (am *DefaultAccountManager) fillEventsWithUserInfo(ctx context.Context, events []*activity.Event, accountId string, user *types.User) error {
|
||||
eventUserInfo, err := am.getEventsUserInfo(ctx, events, accountId, user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, event := range events {
|
||||
initiatorUserInfo, ok := eventUserInfo[event.InitiatorID]
|
||||
if !ok {
|
||||
log.WithContext(ctx).Warnf("failed to resolve user info for initiator: %s", event.InitiatorID)
|
||||
}
|
||||
|
||||
if event.InitiatorEmail == "" && ok {
|
||||
event.InitiatorEmail = initiatorUserInfo.email
|
||||
}
|
||||
|
||||
if event.InitiatorName == "" && ok {
|
||||
// here to allowed to be empty because in the first release we did not store the name
|
||||
event.InitiatorName = initiatorUserInfo.name
|
||||
}
|
||||
|
||||
if ok && event.AccountID != initiatorUserInfo.accountId {
|
||||
if event.Meta == nil {
|
||||
event.Meta = make(map[string]any)
|
||||
}
|
||||
|
||||
event.Meta["external"] = true
|
||||
}
|
||||
|
||||
targetUserInfo, ok := eventUserInfo[event.TargetID]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if event.Meta == nil {
|
||||
event.Meta = make(map[string]any)
|
||||
}
|
||||
|
||||
event.Meta["email"] = targetUserInfo.email
|
||||
event.Meta["username"] = targetUserInfo.name
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (am *DefaultAccountManager) getEventsUserInfo(ctx context.Context, events []*activity.Event, accountId string, user *types.User) (map[string]eventUserInfo, error) {
|
||||
accountUsers, err := am.Store.GetAccountUsers(ctx, store.LockingStrengthShare, accountId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// @note check whether using a external initiator user here is an issue
|
||||
userInfos, err := am.BuildUserInfosForAccount(ctx, accountId, user.Id, accountUsers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
eventUserInfos := make(map[string]eventUserInfo)
|
||||
for i, k := range userInfos {
|
||||
eventUserInfos[i] = eventUserInfo{
|
||||
email: k.Email,
|
||||
name: k.Name,
|
||||
accountId: accountId,
|
||||
}
|
||||
}
|
||||
|
||||
externalUserIds := []string{}
|
||||
for _, event := range events {
|
||||
if _, ok := eventUserInfos[event.InitiatorID]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if event.InitiatorID == activity.SystemInitiator ||
|
||||
event.InitiatorID == accountId ||
|
||||
event.Activity == activity.PeerAddedWithSetupKey {
|
||||
// @todo other events to be excluded if never initiated by a user
|
||||
continue
|
||||
}
|
||||
|
||||
externalUserIds = append(externalUserIds, event.InitiatorID)
|
||||
}
|
||||
|
||||
if len(externalUserIds) == 0 {
|
||||
return eventUserInfos, nil
|
||||
}
|
||||
|
||||
return am.getEventsExternalUserInfo(ctx, externalUserIds, eventUserInfos, user)
|
||||
}
|
||||
|
||||
func (am *DefaultAccountManager) getEventsExternalUserInfo(ctx context.Context, externalUserIds []string, eventUserInfos map[string]eventUserInfo, user *types.User) (map[string]eventUserInfo, error) {
|
||||
externalAccountId := ""
|
||||
fetched := make(map[string]struct{})
|
||||
externalUsers := []*types.User{}
|
||||
for _, id := range externalUserIds {
|
||||
if _, ok := fetched[id]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
externalUser, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, id)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if externalAccountId != "" && externalAccountId != externalUser.AccountID {
|
||||
return nil, fmt.Errorf("multiple external user accounts in events")
|
||||
}
|
||||
|
||||
if externalAccountId == "" {
|
||||
externalAccountId = externalUser.AccountID
|
||||
}
|
||||
|
||||
fetched[id] = struct{}{}
|
||||
externalUsers = append(externalUsers, externalUser)
|
||||
}
|
||||
|
||||
// if we couldn't determine an account, return what we have
|
||||
if externalAccountId == "" {
|
||||
log.WithContext(ctx).Warnf("failed to determine external user account from users: %v", externalUserIds)
|
||||
return eventUserInfos, nil
|
||||
}
|
||||
|
||||
externalUserInfos, err := am.BuildUserInfosForAccount(ctx, externalAccountId, user.Id, externalUsers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i, k := range externalUserInfos {
|
||||
eventUserInfos[i] = eventUserInfo{
|
||||
email: k.Email,
|
||||
name: k.Name,
|
||||
accountId: externalAccountId,
|
||||
}
|
||||
}
|
||||
|
||||
return eventUserInfos, nil
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package events
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
@@ -47,66 +46,15 @@ func (h *handler) getAllEvents(w http.ResponseWriter, r *http.Request) {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
events := make([]*api.Event, len(accountEvents))
|
||||
for i, e := range accountEvents {
|
||||
events[i] = toEventResponse(e)
|
||||
}
|
||||
|
||||
err = h.fillEventsWithUserInfo(r.Context(), events, accountID, userID)
|
||||
if err != nil {
|
||||
util.WriteError(r.Context(), err, w)
|
||||
return
|
||||
}
|
||||
|
||||
util.WriteJSONObject(r.Context(), w, events)
|
||||
}
|
||||
|
||||
func (h *handler) fillEventsWithUserInfo(ctx context.Context, events []*api.Event, accountId, userId string) error {
|
||||
// build email, name maps based on users
|
||||
userInfos, err := h.accountManager.GetUsersFromAccount(ctx, accountId, userId)
|
||||
if err != nil {
|
||||
log.WithContext(ctx).Errorf("failed to get users from account: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
emails := make(map[string]string)
|
||||
names := make(map[string]string)
|
||||
for _, ui := range userInfos {
|
||||
emails[ui.ID] = ui.Email
|
||||
names[ui.ID] = ui.Name
|
||||
}
|
||||
|
||||
var ok bool
|
||||
for _, event := range events {
|
||||
// fill initiator
|
||||
if event.InitiatorEmail == "" {
|
||||
event.InitiatorEmail, ok = emails[event.InitiatorId]
|
||||
if !ok {
|
||||
log.WithContext(ctx).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
|
||||
}
|
||||
|
||||
func toEventResponse(event *activity.Event) *api.Event {
|
||||
meta := make(map[string]string)
|
||||
if event.Meta != nil {
|
||||
|
||||
Reference in New Issue
Block a user