mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-19 16:56:39 +00:00
Optimize Cache and IDP Management (#1147)
This pull request modifies the IdP and cache manager(s) to prevent the sending of app metadata to the upstream IDP on self-hosted instances. As a result, the IdP will now load all users from the IdP without filtering based on accountID. We disable user invites as the administrator's own IDP system manages them.
This commit is contained in:
@@ -5,15 +5,14 @@ import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/telemetry"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/oauth2/google"
|
||||
admin "google.golang.org/api/admin/directory/v1"
|
||||
"google.golang.org/api/googleapi"
|
||||
"google.golang.org/api/option"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/telemetry"
|
||||
)
|
||||
|
||||
// GoogleWorkspaceManager Google Workspace manager client instance.
|
||||
@@ -73,17 +72,13 @@ func NewGoogleWorkspaceManager(config GoogleWorkspaceClientConfig, appMetrics te
|
||||
}
|
||||
|
||||
service, err := admin.NewService(context.Background(),
|
||||
option.WithScopes(admin.AdminDirectoryUserScope, admin.AdminDirectoryUserschemaScope),
|
||||
option.WithScopes(admin.AdminDirectoryUserReadonlyScope),
|
||||
option.WithCredentials(adminCredentials),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = configureAppMetadataSchema(service, config.CustomerID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &GoogleWorkspaceManager{
|
||||
usersService: service.Users,
|
||||
CustomerID: config.CustomerID,
|
||||
@@ -95,27 +90,7 @@ func NewGoogleWorkspaceManager(config GoogleWorkspaceClientConfig, appMetrics te
|
||||
}
|
||||
|
||||
// UpdateUserAppMetadata updates user app metadata based on userID and metadata map.
|
||||
func (gm *GoogleWorkspaceManager) UpdateUserAppMetadata(userID string, appMetadata AppMetadata) error {
|
||||
metadata, err := gm.helper.Marshal(appMetadata)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user := &admin.User{
|
||||
CustomSchemas: map[string]googleapi.RawMessage{
|
||||
"app_metadata": metadata,
|
||||
},
|
||||
}
|
||||
|
||||
_, err = gm.usersService.Update(userID, user).Do()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if gm.appMetrics != nil {
|
||||
gm.appMetrics.IDPMetrics().CountUpdateUserAppMetadata()
|
||||
}
|
||||
|
||||
func (gm *GoogleWorkspaceManager) UpdateUserAppMetadata(_ string, _ AppMetadata) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -130,23 +105,23 @@ func (gm *GoogleWorkspaceManager) GetUserDataByID(userID string, appMetadata App
|
||||
gm.appMetrics.IDPMetrics().CountGetUserDataByID()
|
||||
}
|
||||
|
||||
return parseGoogleWorkspaceUser(user)
|
||||
userData := parseGoogleWorkspaceUser(user)
|
||||
userData.AppMetadata = appMetadata
|
||||
|
||||
return userData, nil
|
||||
}
|
||||
|
||||
// GetAccount returns all the users for a given profile.
|
||||
func (gm *GoogleWorkspaceManager) GetAccount(accountID string) ([]*UserData, error) {
|
||||
query := fmt.Sprintf("app_metadata.wt_account_id=\"%s\"", accountID)
|
||||
usersList, err := gm.usersService.List().Customer(gm.CustomerID).Query(query).Projection("full").Do()
|
||||
usersList, err := gm.usersService.List().Customer(gm.CustomerID).Projection("full").Do()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
usersData := make([]*UserData, 0)
|
||||
for _, user := range usersList.Users {
|
||||
userData, err := parseGoogleWorkspaceUser(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userData := parseGoogleWorkspaceUser(user)
|
||||
userData.AppMetadata.WTAccountID = accountID
|
||||
|
||||
usersData = append(usersData, userData)
|
||||
}
|
||||
@@ -168,61 +143,16 @@ func (gm *GoogleWorkspaceManager) GetAllAccounts() (map[string][]*UserData, erro
|
||||
|
||||
indexedUsers := make(map[string][]*UserData)
|
||||
for _, user := range usersList.Users {
|
||||
userData, err := parseGoogleWorkspaceUser(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)
|
||||
}
|
||||
userData := parseGoogleWorkspaceUser(user)
|
||||
indexedUsers[UnsetAccountID] = append(indexedUsers[UnsetAccountID], userData)
|
||||
}
|
||||
|
||||
return indexedUsers, nil
|
||||
}
|
||||
|
||||
// CreateUser creates a new user in Google Workspace and sends an invitation.
|
||||
func (gm *GoogleWorkspaceManager) CreateUser(email, name, accountID, invitedByEmail string) (*UserData, error) {
|
||||
invite := true
|
||||
metadata := AppMetadata{
|
||||
WTAccountID: accountID,
|
||||
WTPendingInvite: &invite,
|
||||
}
|
||||
|
||||
username := &admin.UserName{}
|
||||
fields := strings.Fields(name)
|
||||
if n := len(fields); n > 0 {
|
||||
username.GivenName = strings.Join(fields[:n-1], " ")
|
||||
username.FamilyName = fields[n-1]
|
||||
}
|
||||
|
||||
payload, err := gm.helper.Marshal(metadata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user := &admin.User{
|
||||
Name: username,
|
||||
PrimaryEmail: email,
|
||||
CustomSchemas: map[string]googleapi.RawMessage{
|
||||
"app_metadata": payload,
|
||||
},
|
||||
Password: GeneratePassword(8, 1, 1, 1),
|
||||
}
|
||||
user, err = gm.usersService.Insert(user).Do()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if gm.appMetrics != nil {
|
||||
gm.appMetrics.IDPMetrics().CountCreateUser()
|
||||
}
|
||||
|
||||
return parseGoogleWorkspaceUser(user)
|
||||
func (gm *GoogleWorkspaceManager) CreateUser(_, _, _, _ string) (*UserData, error) {
|
||||
return nil, fmt.Errorf("method CreateUser not implemented")
|
||||
}
|
||||
|
||||
// GetUserByEmail searches users with a given email.
|
||||
@@ -237,13 +167,8 @@ func (gm *GoogleWorkspaceManager) GetUserByEmail(email string) ([]*UserData, err
|
||||
gm.appMetrics.IDPMetrics().CountGetUserByEmail()
|
||||
}
|
||||
|
||||
userData, err := parseGoogleWorkspaceUser(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
users := make([]*UserData, 0)
|
||||
users = append(users, userData)
|
||||
users = append(users, parseGoogleWorkspaceUser(user))
|
||||
|
||||
return users, nil
|
||||
}
|
||||
@@ -281,8 +206,7 @@ func getGoogleCredentials(serviceAccountKey string) (*google.Credentials, error)
|
||||
creds, err := google.CredentialsFromJSON(
|
||||
context.Background(),
|
||||
decodeKey,
|
||||
admin.AdminDirectoryUserschemaScope,
|
||||
admin.AdminDirectoryUserScope,
|
||||
admin.AdminDirectoryUserReadonlyScope,
|
||||
)
|
||||
if err == nil {
|
||||
// No need to fallback to the default Google credentials path
|
||||
@@ -294,8 +218,7 @@ func getGoogleCredentials(serviceAccountKey string) (*google.Credentials, error)
|
||||
|
||||
creds, err = google.FindDefaultCredentials(
|
||||
context.Background(),
|
||||
admin.AdminDirectoryUserschemaScope,
|
||||
admin.AdminDirectoryUserScope,
|
||||
admin.AdminDirectoryUserReadonlyScope,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -304,62 +227,11 @@ func getGoogleCredentials(serviceAccountKey string) (*google.Credentials, error)
|
||||
return creds, nil
|
||||
}
|
||||
|
||||
// configureAppMetadataSchema create a custom schema for managing app metadata fields in Google Workspace.
|
||||
func configureAppMetadataSchema(service *admin.Service, customerID string) error {
|
||||
schemaList, err := service.Schemas.List(customerID).Do()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// checks if app_metadata schema is already created
|
||||
for _, schema := range schemaList.Schemas {
|
||||
if schema.SchemaName == "app_metadata" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// create new app_metadata schema
|
||||
appMetadataSchema := &admin.Schema{
|
||||
SchemaName: "app_metadata",
|
||||
Fields: []*admin.SchemaFieldSpec{
|
||||
{
|
||||
FieldName: "wt_account_id",
|
||||
FieldType: "STRING",
|
||||
MultiValued: false,
|
||||
},
|
||||
{
|
||||
FieldName: "wt_pending_invite",
|
||||
FieldType: "BOOL",
|
||||
MultiValued: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err = service.Schemas.Insert(customerID, appMetadataSchema).Do()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseGoogleWorkspaceUser parse google user to UserData.
|
||||
func parseGoogleWorkspaceUser(user *admin.User) (*UserData, error) {
|
||||
var appMetadata AppMetadata
|
||||
|
||||
// Get app metadata from custom schemas
|
||||
if user.CustomSchemas != nil {
|
||||
rawMessage := user.CustomSchemas["app_metadata"]
|
||||
helper := JsonParser{}
|
||||
|
||||
if err := helper.Unmarshal(rawMessage, &appMetadata); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func parseGoogleWorkspaceUser(user *admin.User) *UserData {
|
||||
return &UserData{
|
||||
ID: user.Id,
|
||||
Email: user.PrimaryEmail,
|
||||
Name: user.Name.FullName,
|
||||
AppMetadata: appMetadata,
|
||||
}, nil
|
||||
ID: user.Id,
|
||||
Email: user.PrimaryEmail,
|
||||
Name: user.Name.FullName,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user