perf: improve org and org domain creation (#10232)

# Which Problems Are Solved

When an organization domain is verified, e.g. also when creating a new
organization (incl. generated domain), existing usernames are checked if
the domain has been claimed.
The query was not optimized for instances with many users and
organizations.

# How the Problems Are Solved

- Replace the query, which was searching over the users projection with
(computed loginnames) with a dedicated query checking the loginnames
projection directly.
-  All occurrences have been updated to use the new query.

# Additional Changes

None

# Additional Context

- reported through support
- requires backport to v3.x
This commit is contained in:
Livio Spring
2025-07-10 11:17:49 -04:00
committed by GitHub
parent 0598abe7e6
commit fefeaea56a
7 changed files with 110 additions and 71 deletions

View File

@@ -1044,6 +1044,70 @@ func TestServer_AddOrganizationDomain(t *testing.T) {
}
}
func TestServer_AddOrganizationDomain_ClaimDomain(t *testing.T) {
domain := gofakeit.DomainName()
// create an organization, ensure it has globally unique usernames
// and create a user with a loginname that matches the domain later on
organization, err := Client.CreateOrganization(CTX, &v2beta_org.CreateOrganizationRequest{
Name: gofakeit.AppName(),
})
require.NoError(t, err)
_, err = Instance.Client.Admin.AddCustomDomainPolicy(CTX, &admin.AddCustomDomainPolicyRequest{
OrgId: organization.GetId(),
UserLoginMustBeDomain: false,
})
require.NoError(t, err)
username := gofakeit.Username() + "@" + domain
ownUser := Instance.CreateHumanUserVerified(CTX, organization.GetId(), username, "")
// create another organization, ensure it has globally unique usernames
// and create a user with a loginname that matches the domain later on
otherOrg, err := Client.CreateOrganization(CTX, &v2beta_org.CreateOrganizationRequest{
Name: gofakeit.AppName(),
})
require.NoError(t, err)
_, err = Instance.Client.Admin.AddCustomDomainPolicy(CTX, &admin.AddCustomDomainPolicyRequest{
OrgId: otherOrg.GetId(),
UserLoginMustBeDomain: false,
})
require.NoError(t, err)
otherUsername := gofakeit.Username() + "@" + domain
otherUser := Instance.CreateHumanUserVerified(CTX, otherOrg.GetId(), otherUsername, "")
// if we add the domain now to the first organization, it should be claimed on the second organization, resp. its user(s)
_, err = Client.AddOrganizationDomain(CTX, &v2beta_org.AddOrganizationDomainRequest{
OrganizationId: organization.GetId(),
Domain: domain,
})
require.NoError(t, err)
// check both users: the first one must be untouched, the second one must be updated
users, err := Instance.Client.UserV2.ListUsers(CTX, &user.ListUsersRequest{
Queries: []*user.SearchQuery{
{
Query: &user.SearchQuery_InUserIdsQuery{
InUserIdsQuery: &user.InUserIDQuery{UserIds: []string{ownUser.GetUserId(), otherUser.GetUserId()}},
},
},
},
})
require.NoError(t, err)
require.Len(t, users.GetResult(), 2)
for _, u := range users.GetResult() {
if u.GetUserId() == ownUser.GetUserId() {
assert.Equal(t, username, u.GetPreferredLoginName())
continue
}
if u.GetUserId() == otherUser.GetUserId() {
assert.NotEqual(t, otherUsername, u.GetPreferredLoginName())
assert.Contains(t, u.GetPreferredLoginName(), "@temporary.")
}
}
}
func TestServer_ListOrganizationDomains(t *testing.T) {
domain := gofakeit.URL()
tests := []struct {

View File

@@ -250,26 +250,5 @@ func createOrganizationRequestAdminToCommand(admin *v2beta_org.CreateOrganizatio
}
func (s *Server) getClaimedUserIDsOfOrgDomain(ctx context.Context, orgDomain, orgID string) ([]string, error) {
queries := make([]query.SearchQuery, 0, 2)
loginName, err := query.NewUserPreferredLoginNameSearchQuery("@"+orgDomain, query.TextEndsWithIgnoreCase)
if err != nil {
return nil, err
}
queries = append(queries, loginName)
if orgID != "" {
owner, err := query.NewUserResourceOwnerSearchQuery(orgID, query.TextNotEquals)
if err != nil {
return nil, err
}
queries = append(queries, owner)
}
users, err := s.query.SearchUsers(ctx, &query.UserSearchQueries{Queries: queries}, nil)
if err != nil {
return nil, err
}
userIDs := make([]string, len(users.Users))
for i, user := range users.Users {
userIDs[i] = user.ID
}
return userIDs, nil
return s.query.SearchClaimedUserIDsOfOrgDomain(ctx, orgDomain, orgID)
}