Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[management] Remove redundant get account calls in GetAccountFromToken #2615

Merged
merged 29 commits into from
Sep 27, 2024

Conversation

bcmmbaga
Copy link
Contributor

Describe your changes

Issue ticket number and link

Checklist

  • Is it a bug fix
  • Is a typo/documentation fix
  • Is a feature enhancement
  • It is a refactor
  • Created tests that fail without the change (if possible)
  • Extended the README / documentation, if necessary

@bcmmbaga bcmmbaga marked this pull request as ready for review September 18, 2024 16:30
@bcmmbaga bcmmbaga force-pushed the refactor-get-account-by-token branch 2 times, most recently from 4b8680c to 9631cb4 Compare September 20, 2024 08:49
@bcmmbaga bcmmbaga force-pushed the refactor-get-account-by-token branch 2 times, most recently from 7d870f6 to 1d6f4a4 Compare September 22, 2024 20:23
Signed-off-by: bcmmbaga <[email protected]>
management/server/account.go Outdated Show resolved Hide resolved
management/server/account.go Outdated Show resolved Hide resolved
Comment on lines +1817 to +1844
unlock := am.Store.AcquireWriteLockByUID(ctx, accountID)
defer unlock()

account, err := am.Store.GetAccount(ctx, accountID)
if err != nil {
return err
}

jwtGroupsNames := extractJWTGroups(ctx, settings.JWTGroupsClaimName, claims)

oldGroups := make([]string, len(user.AutoGroups))
copy(oldGroups, user.AutoGroups)

// Update the account if group membership changes
if account.SetJWTGroups(claims.UserId, jwtGroupsNames) {
addNewGroups := difference(user.AutoGroups, oldGroups)
removeOldGroups := difference(oldGroups, user.AutoGroups)

if settings.GroupsPropagationEnabled {
account.UserGroupsAddToPeers(claims.UserId, addNewGroups...)
account.UserGroupsRemoveFromPeers(claims.UserId, removeOldGroups...)
account.Network.IncSerial()
}
if claim, ok := claims.Raw[account.Settings.JWTGroupsClaimName]; ok {
if slice, ok := claim.([]interface{}); ok {
var groupsNames []string
for _, item := range slice {
if g, ok := item.(string); ok {
groupsNames = append(groupsNames, g)
} else {
log.WithContext(ctx).Errorf("JWT claim %q is not a string: %v", account.Settings.JWTGroupsClaimName, item)
}
}

oldGroups := make([]string, len(user.AutoGroups))
copy(oldGroups, user.AutoGroups)
// if groups were added or modified, save the account
if account.SetJWTGroups(claims.UserId, groupsNames) {
if account.Settings.GroupsPropagationEnabled {
if user, err := account.FindUser(claims.UserId); err == nil {
addNewGroups := difference(user.AutoGroups, oldGroups)
removeOldGroups := difference(oldGroups, user.AutoGroups)
account.UserGroupsAddToPeers(claims.UserId, addNewGroups...)
account.UserGroupsRemoveFromPeers(claims.UserId, removeOldGroups...)
account.Network.IncSerial()
if err := am.Store.SaveAccount(ctx, account); err != nil {
log.WithContext(ctx).Errorf("failed to save account: %v", err)
} else {
log.WithContext(ctx).Tracef("user %s: JWT group membership changed, updating account peers", claims.UserId)
am.updateAccountPeers(ctx, account)
unlock()
alreadyUnlocked = true
for _, g := range addNewGroups {
if group := account.GetGroup(g); group != nil {
am.StoreEvent(ctx, user.Id, user.Id, account.Id, activity.GroupAddedToUser,
map[string]any{
"group": group.Name,
"group_id": group.ID,
"is_service_user": user.IsServiceUser,
"user_name": user.ServiceUserName})
}
}
for _, g := range removeOldGroups {
if group := account.GetGroup(g); group != nil {
am.StoreEvent(ctx, user.Id, user.Id, account.Id, activity.GroupRemovedFromUser,
map[string]any{
"group": group.Name,
"group_id": group.ID,
"is_service_user": user.IsServiceUser,
"user_name": user.ServiceUserName})
}
}
}
}
} else {
if err := am.Store.SaveAccount(ctx, account); err != nil {
log.WithContext(ctx).Errorf("failed to save account: %v", err)
}
}
}
} else {
log.WithContext(ctx).Debugf("JWT claim %q is not a string array", account.Settings.JWTGroupsClaimName)
if err := am.Store.SaveAccount(ctx, account); err != nil {
log.WithContext(ctx).Errorf("failed to save account: %v", err)
return nil
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idealy this would get wrapped in a transaction and we completly get rid of the lock and the getAccount

Comment on lines +1943 to +1959
unlockAccount := am.Store.AcquireWriteLockByUID(ctx, userAccountID)
defer unlockAccount()
account, err = am.Store.GetAccountByUser(ctx, claims.UserId)
account, err := am.Store.GetAccountByUser(ctx, claims.UserId)
if err != nil {
return nil, err
return "", err
}
// If there is no primary domain account yet, we set the account as primary for the domain. Otherwise,
// we compare the account's ID with the domain account ID, and if they don't match, we set the account as
// non-primary account for the domain. We don't merge accounts at this stage, because of cases when a domain
// was previously unclassified or classified as public so N users that logged int that time, has they own account
// and peers that shouldn't be lost.
primaryDomain := domainAccount == nil || account.Id == domainAccount.Id

err = am.handleExistingUserAccount(ctx, account, primaryDomain, claims)
if err != nil {
return nil, err
primaryDomain := domainAccountID == "" || account.Id == domainAccountID
if err = am.handleExistingUserAccount(ctx, account, primaryDomain, claims); err != nil {
return "", err
}
return account, nil

return account.Id, nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not do a getAccount and pass it down. We should do the updates in handleExistingUserAccount within a transaction inside that method

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can go into another PR

Comment on lines +1961 to +1975
var domainAccount *Account
if domainAccountID != "" {
unlockAccount := am.Store.AcquireWriteLockByUID(ctx, domainAccountID)
defer unlockAccount()
domainAccount, err = am.Store.GetAccountByPrivateDomain(ctx, claims.Domain)
if err != nil {
return nil, err
return "", err
}
}
return am.handleNewUserAccount(ctx, domainAccount, claims)

account, err := am.handleNewUserAccount(ctx, domainAccount, claims)
if err != nil {
return "", err
}
return account.Id, nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same. We should not get the account here and do the update with transactions inside the handleNewUserAccount

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can go into another PR

management/server/group.go Outdated Show resolved Hide resolved
Comment on lines +150 to +160
account, err := h.accountManager.GetAccountByID(r.Context(), accountID, userID)
if err != nil {
util.WriteError(r.Context(), err, w)
return
}

if r.Method == http.MethodGet {
h.getPeer(r.Context(), account, peerID, userID, w)
} else {
h.updatePeer(r.Context(), account, userID, peerID, w, r)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not need to get the whole account to update or get a single peer

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also make the switch nicely split between http.MethodGet, http.MethodPut without the additional if

if err != nil {
util.WriteError(r.Context(), err, w)
return
}

peers, err := h.accountManager.GetPeers(r.Context(), account.Id, user.Id)
account, err := h.accountManager.GetAccountByID(r.Context(), accountID, userID)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should not get account here, we should only get peer and pass accountID into the GetValidatedPeers and do seperate db calls for the tables there (probably also in a transaction)

management/server/http/policies_handler.go Outdated Show resolved Hide resolved
@@ -350,15 +351,17 @@ func (am *DefaultAccountManager) SavePolicy(ctx context.Context, accountID, user
return err
}

exists := am.savePolicy(account, policy)
if err = am.savePolicy(account, policy, isUpdate); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should not load the whole account. Instead have a transaction and update only the fields

management/server/sql_store.go Outdated Show resolved Hide resolved
management/server/sql_store.go Outdated Show resolved Hide resolved
management/server/sql_store.go Outdated Show resolved Hide resolved
management/server/sql_store.go Outdated Show resolved Hide resolved
Signed-off-by: bcmmbaga <[email protected]>
pascal-fischer
pascal-fischer previously approved these changes Sep 26, 2024
Signed-off-by: bcmmbaga <[email protected]>
Signed-off-by: bcmmbaga <[email protected]>
Copy link

sonarcloud bot commented Sep 26, 2024

@bcmmbaga bcmmbaga merged commit acb73bd into main Sep 27, 2024
21 checks passed
@bcmmbaga bcmmbaga deleted the refactor-get-account-by-token branch September 27, 2024 14:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants