diff --git a/pkg/keystone/models/identity_provider.go b/pkg/keystone/models/identity_provider.go index 69be6fe2a9..a721e32204 100644 --- a/pkg/keystone/models/identity_provider.go +++ b/pkg/keystone/models/identity_provider.go @@ -860,7 +860,7 @@ func (self *SIdentityProvider) Purge(ctx context.Context, userCred mcclient.Toke if self.isSsoIdp() && self.AutoCreateUser.IsFalse() { continue } - err = users[i].ValidatePurgeCondition(ctx) + err = users[i].ValidatePurgeCondition(ctx, nil) if err != nil { db.OpsLog.LogEvent(&users[i], db.ACT_DELETE_FAIL, err, userCred) log.Errorf("users %s ValidatePurgeCondition fail %s", users[i].Name, err) diff --git a/pkg/keystone/models/users.go b/pkg/keystone/models/users.go index 2f9a416b4e..64e541c9ab 100644 --- a/pkg/keystone/models/users.go +++ b/pkg/keystone/models/users.go @@ -22,6 +22,7 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/gotypes" "yunion.io/x/pkg/tristate" "yunion.io/x/pkg/util/rbacscope" "yunion.io/x/sqlchemy" @@ -631,6 +632,40 @@ func (user *SUser) GetCredentialCount() (int, error) { return q.CountWithError() } +func (manager *SUserManager) FetchScopeResources(userIds []string) (map[string]api.ExternalResourceInfo, error) { + resources := ScopeResourceManager.Query().In("owner_id", userIds).SubQuery() + q := resources.Query( + resources.Field("resource"), + resources.Field("owner_id"), + sqlchemy.SUM("res_count", resources.Field("count")), + sqlchemy.MAX("last_update", resources.Field("updated_at")), + ) + q = q.GroupBy(resources.Field("resource")) + ret := []struct { + Resource string + OwnerId string + ResCount int + LastUpdate time.Time + }{} + err := q.All(&ret) + if err != nil { + return nil, err + } + result := map[string]api.ExternalResourceInfo{} + for _, res := range ret { + _, ok := result[res.OwnerId] + if !ok && res.ResCount > 0 { + result[res.OwnerId] = api.ExternalResourceInfo{ + ExtResource: map[string]int{}, + ExtResourcesLastUpdate: res.LastUpdate, + ExtResourcesNextUpdate: res.LastUpdate.Add(time.Duration(options.Options.FetchScopeResourceCountIntervalSeconds) * time.Second), + } + } + result[res.OwnerId].ExtResource[res.Resource] = res.ResCount + } + return result, nil +} + func (manager *SUserManager) FetchCustomizeColumns( ctx context.Context, userCred mcclient.TokenCredential, @@ -658,7 +693,14 @@ func (manager *SUserManager) FetchCustomizeColumns( return rows } + scopeResources, err := manager.FetchScopeResources(userIds) + if err != nil { + log.Errorf("FetchScopeResources error: %v", err) + return rows + } + for i := range rows { + rows[i].ExternalResourceInfo, _ = scopeResources[userIds[i]] if idps, ok := idpsMaps[userIds[i]]; ok { if len(idps) > 0 { // rows[i].IdpResourceInfo = idps[0].IdpResourceInfo @@ -697,17 +739,6 @@ func userExtra(ctx context.Context, userCred mcclient.TokenCredential, user *SUs out.IsLocal = false } - external, update, _ := user.getExternalResources() - if len(external) > 0 { - out.ExtResource = external - out.ExtResourcesLastUpdate = update - if update.IsZero() { - update = time.Now() - } - nextUpdate := update.Add(time.Duration(options.Options.FetchScopeResourceCountIntervalSeconds) * time.Second) - out.ExtResourcesNextUpdate = nextUpdate - } - projects, _ := ProjectManager.FetchUserProjects(user.Id) out.Projects = make([]api.SFetchDomainObjectWithMetadata, len(projects)) for i, proj := range projects { @@ -815,7 +846,7 @@ func (user *SUser) PostUpdate(ctx context.Context, userCred mcclient.TokenCreden } } -func (user *SUser) ValidateDeleteCondition(ctx context.Context, info jsonutils.JSONObject) error { +func (user *SUser) ValidateDeleteCondition(ctx context.Context, info *api.UserDetails) error { idMappings, err := user.getIdmappings() if err != nil { return errors.Wrap(err, "getIdmappings") @@ -831,21 +862,32 @@ func (user *SUser) ValidateDeleteCondition(ctx context.Context, info jsonutils.J } } } - err = user.ValidatePurgeCondition(ctx) + err = user.ValidatePurgeCondition(ctx, info) if err != nil { - return errors.Wrap(err, "ValidatePurgeCondition") + return err } return user.SIdentityBaseResource.ValidateDeleteCondition(ctx, nil) } -func (user *SUser) ValidatePurgeCondition(ctx context.Context) error { - external, _, _ := user.getExternalResources() - if len(external) > 0 { - return httperrors.NewNotEmptyError("user contains external resources") - } +func (user *SUser) ValidatePurgeCondition(ctx context.Context, info *api.UserDetails) error { if user.IsAdminUser() { return httperrors.NewForbiddenError("cannot delete system user") } + if gotypes.IsNil(info) { + info = &api.UserDetails{} + scopResource, err := UserManager.FetchScopeResources([]string{user.Id}) + if err != nil { + return errors.Wrapf(err, "FetchScopeResources") + } + info.ExternalResourceInfo, _ = scopResource[user.Id] + } + if len(info.ExtResource) > 0 { + for k, cnt := range info.ExtResource { + if cnt > 0 { + return httperrors.NewNotEmptyError("user contains %d external resources %s", cnt, k) + } + } + } return nil }