wip: Handle user metadata without transmitting them to Identity Provider

This commit is contained in:
bcmmbaga
2023-09-14 12:49:35 +03:00
parent bb791d59f3
commit 50ecf6f4da
3 changed files with 158 additions and 113 deletions

View File

@@ -4,6 +4,7 @@ import (
"context"
"crypto/sha256"
b64 "encoding/base64"
"errors"
"fmt"
"hash/crc32"
"math/rand"
@@ -938,6 +939,37 @@ func (am *DefaultAccountManager) newAccount(userID, domain string) (*Account, er
func (am *DefaultAccountManager) warmupIDPCache() error {
userData, err := am.idpManager.GetAllAccounts()
if err != nil {
var idpErr *idp.Error
if errors.As(err, &idpErr) {
log.Warnf("failed to fetch all accounts from idp: %v", idpErr)
log.Info("warming cache using store")
accounts := am.Store.GetAllAccounts()
for _, account := range accounts {
data := make([]*idp.UserData, 0)
for _, user := range account.Users {
// fetch user info with idp manager
userData, err := am.idpManager.GetUserDataByID(user.Id, idp.AppMetadata{WTAccountID: account.Id})
if err != nil {
return err
}
// update user metadata as are not stored in upstream idp
// Note (self-hosted does not support pendingInvite and invitedBy status)
userData.AppMetadata.WTAccountID = account.Id
data = append(data, userData)
}
err = am.cacheManager.Set(am.ctx, account.Id, data, cacheStore.WithExpiration(cacheEntryExpiration()))
if err != nil {
return err
}
}
return nil
}
return err
}

View File

@@ -12,9 +12,10 @@ import (
"time"
"github.com/golang-jwt/jwt"
"github.com/netbirdio/netbird/management/server/telemetry"
log "github.com/sirupsen/logrus"
"goauthentik.io/api/v3"
"github.com/netbirdio/netbird/management/server/telemetry"
)
// AuthentikManager authentik manager client instance.
@@ -210,47 +211,48 @@ func (ac *AuthentikCredentials) Authenticate() (JWTToken, error) {
// UpdateUserAppMetadata updates user app metadata based on userID and metadata map.
func (am *AuthentikManager) UpdateUserAppMetadata(userID string, appMetadata AppMetadata) error {
ctx, err := am.authenticationContext()
if err != nil {
return err
}
userPk, err := strconv.ParseInt(userID, 10, 32)
if err != nil {
return err
}
var pendingInvite bool
if appMetadata.WTPendingInvite != nil {
pendingInvite = *appMetadata.WTPendingInvite
}
patchedUserReq := api.PatchedUserRequest{
Attributes: map[string]interface{}{
wtAccountID: appMetadata.WTAccountID,
wtPendingInvite: pendingInvite,
},
}
_, resp, err := am.apiClient.CoreApi.CoreUsersPartialUpdate(ctx, int32(userPk)).
PatchedUserRequest(patchedUserReq).
Execute()
if err != nil {
return err
}
defer resp.Body.Close()
if am.appMetrics != nil {
am.appMetrics.IDPMetrics().CountUpdateUserAppMetadata()
}
if resp.StatusCode != http.StatusOK {
if am.appMetrics != nil {
am.appMetrics.IDPMetrics().CountRequestStatusError()
}
return fmt.Errorf("unable to update user %s, statusCode %d", userID, resp.StatusCode)
}
return nil
//ctx, err := am.authenticationContext()
//if err != nil {
// return err
//}
//
//userPk, err := strconv.ParseInt(userID, 10, 32)
//if err != nil {
// return err
//}
//
//var pendingInvite bool
//if appMetadata.WTPendingInvite != nil {
// pendingInvite = *appMetadata.WTPendingInvite
//}
//
//patchedUserReq := api.PatchedUserRequest{
// Attributes: map[string]interface{}{
// wtAccountID: appMetadata.WTAccountID,
// wtPendingInvite: pendingInvite,
// },
//}
//_, resp, err := am.apiClient.CoreApi.CoreUsersPartialUpdate(ctx, int32(userPk)).
// PatchedUserRequest(patchedUserReq).
// Execute()
//if err != nil {
// return err
//}
//defer resp.Body.Close()
//
//if am.appMetrics != nil {
// am.appMetrics.IDPMetrics().CountUpdateUserAppMetadata()
//}
//
//if resp.StatusCode != http.StatusOK {
// if am.appMetrics != nil {
// am.appMetrics.IDPMetrics().CountRequestStatusError()
// }
// return fmt.Errorf("unable to update user %s, statusCode %d", userID, resp.StatusCode)
//}
//
//return nil
return &Error{"UpdateUserAppMetadata is not implemented"}
}
// GetUserDataByID requests user data from authentik via ID.
@@ -287,83 +289,86 @@ func (am *AuthentikManager) GetUserDataByID(userID string, appMetadata AppMetada
// GetAccount returns all the users for a given profile.
func (am *AuthentikManager) GetAccount(accountID string) ([]*UserData, error) {
ctx, err := am.authenticationContext()
if err != nil {
return nil, err
}
accountFilter := fmt.Sprintf("{%q:%q}", wtAccountID, accountID)
userList, resp, err := am.apiClient.CoreApi.CoreUsersList(ctx).Attributes(accountFilter).Execute()
if err != nil {
return nil, err
}
defer resp.Body.Close()
if am.appMetrics != nil {
am.appMetrics.IDPMetrics().CountGetAccount()
}
if resp.StatusCode != http.StatusOK {
if am.appMetrics != nil {
am.appMetrics.IDPMetrics().CountRequestStatusError()
}
return nil, fmt.Errorf("unable to get account %s users, statusCode %d", accountID, resp.StatusCode)
}
users := make([]*UserData, 0)
for _, user := range userList.Results {
userData, err := parseAuthentikUser(user)
if err != nil {
return nil, err
}
users = append(users, userData)
}
return users, nil
//ctx, err := am.authenticationContext()
//if err != nil {
// return nil, err
//}
//
//accountFilter := fmt.Sprintf("{%q:%q}", wtAccountID, accountID)
//userList, resp, err := am.apiClient.CoreApi.CoreUsersList(ctx).Attributes(accountFilter).Execute()
//if err != nil {
// return nil, err
//}
//defer resp.Body.Close()
//
//if am.appMetrics != nil {
// am.appMetrics.IDPMetrics().CountGetAccount()
//}
//
//if resp.StatusCode != http.StatusOK {
// if am.appMetrics != nil {
// am.appMetrics.IDPMetrics().CountRequestStatusError()
// }
// return nil, fmt.Errorf("unable to get account %s users, statusCode %d", accountID, resp.StatusCode)
//}
//
//users := make([]*UserData, 0)
//for _, user := range userList.Results {
// userData, err := parseAuthentikUser(user)
// if err != nil {
// return nil, err
// }
// users = append(users, userData)
//}
//
//return users, nil
return nil, &Error{"GetAccount is not implemented"}
}
// GetAllAccounts gets all registered accounts with corresponding user data.
// It returns a list of users indexed by accountID.
func (am *AuthentikManager) GetAllAccounts() (map[string][]*UserData, error) {
ctx, err := am.authenticationContext()
if err != nil {
return nil, err
}
//ctx, err := am.authenticationContext()
//if err != nil {
// return nil, err
//}
//
//userList, resp, err := am.apiClient.CoreApi.CoreUsersList(ctx).Execute()
//if err != nil {
// return nil, err
//}
//defer resp.Body.Close()
//
//if am.appMetrics != nil {
// am.appMetrics.IDPMetrics().CountGetAllAccounts()
//}
//
//if resp.StatusCode != http.StatusOK {
// if am.appMetrics != nil {
// am.appMetrics.IDPMetrics().CountRequestStatusError()
// }
// return nil, fmt.Errorf("unable to get all accounts, statusCode %d", resp.StatusCode)
//}
//
//indexedUsers := make(map[string][]*UserData)
//for _, user := range userList.Results {
// userData, err := parseAuthentikUser(user)
// if err != nil {
// return nil, err
// }
//
// accountID := userData.AppMetadata.WTAccountID
// if accountID != "" {
// if _, ok := indexedUsers[accountID]; !ok {
// indexedUsers[accountID] = make([]*UserData, 0)
// }
// indexedUsers[accountID] = append(indexedUsers[accountID], userData)
// }
//}
//
//return indexedUsers, nil
userList, resp, err := am.apiClient.CoreApi.CoreUsersList(ctx).Execute()
if err != nil {
return nil, err
}
defer resp.Body.Close()
if am.appMetrics != nil {
am.appMetrics.IDPMetrics().CountGetAllAccounts()
}
if resp.StatusCode != http.StatusOK {
if am.appMetrics != nil {
am.appMetrics.IDPMetrics().CountRequestStatusError()
}
return nil, fmt.Errorf("unable to get all accounts, statusCode %d", resp.StatusCode)
}
indexedUsers := make(map[string][]*UserData)
for _, user := range userList.Results {
userData, err := parseAuthentikUser(user)
if err != nil {
return nil, err
}
accountID := userData.AppMetadata.WTAccountID
if accountID != "" {
if _, ok := indexedUsers[accountID]; !ok {
indexedUsers[accountID] = make([]*UserData, 0)
}
indexedUsers[accountID] = append(indexedUsers[accountID], userData)
}
}
return indexedUsers, nil
return nil, &Error{"GetAllAccounts is not implemented"}
}
// CreateUser creates a new user in authentik Idp and sends an invitation.

View File

@@ -9,6 +9,14 @@ import (
"github.com/netbirdio/netbird/management/server/telemetry"
)
type Error struct {
message string
}
func (e *Error) Error() string {
return e.message
}
// Manager idp manager interface
type Manager interface {
UpdateUserAppMetadata(userId string, appMetadata AppMetadata) error