Handle user delete (#1113)

Implement user deletion across all IDP-ss. Expires all user peers
when the user is deleted. Users are permanently removed from a local
store, but in IDP, we remove Netbird attributes for the user
untilUserDeleteFromIDPEnabled setting is not enabled.

To test, an admin user should remove any additional users.

Until the UI incorporates this feature, use a curl DELETE request
targeting the /users/<USER_ID> management endpoint. Note that this
request only removes user attributes and doesn't trigger a delete
from the IDP.

To enable user removal from the IdP, set UserDeleteFromIDPEnabled
to true in account settings. Until we have a UI for this, make this
change directly in the store file.

Store the deleted email addresses in encrypted in activity store.
This commit is contained in:
Givi Khojanashvili
2023-09-19 20:08:40 +04:00
committed by GitHub
parent 8febab4076
commit d4b6d7646c
35 changed files with 744 additions and 112 deletions

0
management/server/http/api/generate.sh Normal file → Executable file
View File

View File

@@ -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_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
example: demo@netbird.io
target_id:
description: The ID of the target of the event. E.g., an ID of the peer that a user removed.
type: string
@@ -938,6 +942,7 @@ components:
- activity
- activity_code
- initiator_id
- initiator_email
- target_id
- meta
responses:

View File

@@ -164,6 +164,9 @@ type Event struct {
// Id Event unique identifier
Id string `json:"id"`
// InitiatorEmail The e-mail address of the initiator of the event. E.g., an e-mail of a user that triggered the event.
InitiatorEmail string `json:"initiator_email"`
// 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"`

View File

@@ -45,14 +45,46 @@ func (h *EventsHandler) GetAllEvents(w http.ResponseWriter, r *http.Request) {
util.WriteError(err, w)
return
}
events := make([]*api.Event, 0)
for _, e := range accountEvents {
events = append(events, toEventResponse(e))
events := make([]*api.Event, len(accountEvents))
for i, e := range accountEvents {
events[i] = toEventResponse(e)
}
err = h.fillEventsWithInitiatorEmail(events, account.Id, user.Id)
if err != nil {
util.WriteError(err, w)
return
}
util.WriteJSONObject(w, events)
}
func (h *EventsHandler) fillEventsWithInitiatorEmail(events []*api.Event, accountId, userId string) error {
// build email map based on users
userInfos, err := h.accountManager.GetUsersFromAccount(accountId, userId)
if err != nil {
log.Errorf("failed to get users from account: %s", err)
return err
}
emails := make(map[string]string)
for _, ui := range userInfos {
emails[ui.ID] = ui.Email
}
// fill event with email of initiator
var ok bool
for _, event := range events {
if event.InitiatorEmail == "" {
event.InitiatorEmail, ok = emails[event.InitiatorId]
if !ok {
log.Warnf("failed to resolve email for initiator: %s", event.InitiatorId)
}
}
}
return nil
}
func toEventResponse(event *activity.Event) *api.Event {
meta := make(map[string]string)
if event.Meta != nil {
@@ -60,13 +92,15 @@ func toEventResponse(event *activity.Event) *api.Event {
meta[s] = fmt.Sprintf("%v", a)
}
}
return &api.Event{
Id: fmt.Sprint(event.ID),
InitiatorId: event.InitiatorID,
Activity: event.Activity.Message(),
ActivityCode: api.EventActivityCode(event.Activity.StringCode()),
TargetId: event.TargetID,
Timestamp: event.Timestamp,
Meta: meta,
e := &api.Event{
Id: fmt.Sprint(event.ID),
InitiatorId: event.InitiatorID,
InitiatorEmail: event.InitiatorEmail,
Activity: event.Activity.Message(),
ActivityCode: api.EventActivityCode(event.Activity.StringCode()),
TargetId: event.TargetID,
Timestamp: event.Timestamp,
Meta: meta,
}
return e
}

View File

@@ -37,6 +37,9 @@ func initEventsTestData(account string, user *server.User, events ...*activity.E
},
}, user, nil
},
GetUsersFromAccountFunc: func(accountID, userID string) ([]*server.UserInfo, error) {
return make([]*server.UserInfo, 0), nil
},
},
claimsExtractor: jwtclaims.NewClaimsExtractor(
jwtclaims.WithFromRequestContext(func(r *http.Request) jwtclaims.AuthorizationClaims {