Files
cloudpods/pkg/notify/models/subscriber.go

643 lines
22 KiB
Go

// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package models
import (
"context"
"database/sql"
"fmt"
"sort"
"strings"
"golang.org/x/sync/errgroup"
"k8s.io/apimachinery/pkg/util/sets"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/tristate"
"yunion.io/x/pkg/utils"
"yunion.io/x/sqlchemy"
api "yunion.io/x/onecloud/pkg/apis/notify"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/auth"
"yunion.io/x/onecloud/pkg/mcclient/modules"
"yunion.io/x/onecloud/pkg/util/logclient"
"yunion.io/x/onecloud/pkg/util/rbacutils"
"yunion.io/x/onecloud/pkg/util/stringutils2"
)
var SubscriberManager *SSubscriberManager
func init() {
SubscriberManager = &SSubscriberManager{
SStandaloneAnonResourceBaseManager: db.NewStandaloneAnonResourceBaseManager(
SSubscriber{},
"subscriber_tbl",
"subscriber",
"subscribers",
),
}
SubscriberManager.SetVirtualObject(SubscriberManager)
}
type SSubscriberManager struct {
db.SStandaloneAnonResourceBaseManager
db.SEnabledResourceBaseManager
}
type SSubscriber struct {
db.SStandaloneAnonResourceBase
db.SEnabledResourceBase
TopicID string `width:"128" charset:"ascii" nullable:"false" index:"true" get:"user" list:"user" create:"required"`
Type string `width:"16" charset:"ascii" nullable:"false" index:"true" get:"user" list:"user" create:"required"`
Identification string `width:"128" charset:"ascii" nullable:"false" index:"true"`
RoleScope string `width:"8" charset:"ascii" nullable:"false" get:"user" list:"user" create:"optional"`
ResourceScope string `width:"8" charset:"ascii" nullable:"false" get:"user" list:"user" create:"required"`
ResourceAttributionId string `width:"128" charset:"ascii" nullable:"false" get:"user" list:"user" create:"optional"`
ResourceAttributionName string `width:"128" charset:"utf8" list:"user" create:"optional"`
Scope string `width:"128" charset:"ascii" nullable:"false" create:"required"`
DomainId string `width:"128" charset:"ascii" nullable:"false" create:"optional"`
}
func (sm *SSubscriberManager) validateReceivers(ctx context.Context, receivers []string) ([]string, error) {
rs, err := ReceiverManager.FetchByIdOrNames(ctx, receivers...)
if err != nil {
return nil, errors.Wrap(err, "unable to fetch Receivers")
}
reSet := sets.NewString(receivers...)
reIds := make([]string, len(rs))
for i := range rs {
reSet.Delete(rs[i].GetId())
reSet.Delete(rs[i].GetName())
reIds[i] = rs[i].GetId()
}
if reSet.Len() > 0 {
return nil, httperrors.NewInputParameterError("receivers %q not found", strings.Join(reSet.UnsortedList(), ", "))
}
return reIds, nil
}
func (sm *SSubscriberManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.SubscriberCreateInput) (api.SubscriberCreateInput, error) {
var err error
// permission check
sSystem, sDomain := string(rbacutils.ScopeSystem), string(rbacutils.ScopeDomain)
switch input.Scope {
case sSystem:
allow := db.IsAdminAllowCreate(userCred, sm)
if !allow {
return input, httperrors.NewForbiddenError("The scope %s and the role of the operator do not match", input.Scope)
}
case sDomain:
allow := db.IsDomainAllowCreate(userCred, sm)
if !allow {
return input, httperrors.NewForbiddenError("The scope %s and the role of the operator do not match", input.Scope)
}
default:
return input, httperrors.NewInputParameterError("unknown scope %s", input.Scope)
}
input.StandaloneAnonResourceCreateInput, err = sm.SStandaloneAnonResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.StandaloneAnonResourceCreateInput)
if err != nil {
return input, errors.Wrap(err, "SVirtualResourceBaseManager.ValidateCreateData")
}
// check topic
t, err := TopicManager.FetchById(input.TopicID)
if err != nil {
return input, errors.Wrapf(err, "unable to fetch topic %s", input.TopicID)
}
// check resource scope
if !utils.IsInStringArray(input.ResourceScope, []string{api.SUBSCRIBER_SCOPE_SYSTEM, api.SUBSCRIBER_SCOPE_DOMAIN, api.SUBSCRIBER_SCOPE_PROJECT}) {
return input, httperrors.NewInputParameterError("unknown resource_scope %q", input.ResourceScope)
}
// resource Attribution Id
var domainId string
switch input.ResourceScope {
case api.SUBSCRIBER_SCOPE_SYSTEM:
input.ResourceAttributionId = ""
input.DomainId = ""
case api.SUBSCRIBER_SCOPE_PROJECT:
tenant, err := db.TenantCacheManager.FetchTenantByIdOrName(ctx, input.ResourceAttributionId)
if err != nil {
return input, errors.Wrapf(err, "unable to fetch project %s", input.ResourceAttributionId)
}
domainId = tenant.DomainId
input.DomainId = domainId
input.ResourceAttributionId = tenant.GetId()
input.ResourceAttributionName = tenant.GetName()
case api.SUBSCRIBER_SCOPE_DOMAIN:
tenant, err := db.TenantCacheManager.FetchDomainByIdOrName(ctx, input.ResourceAttributionId)
if err != nil {
return input, errors.Wrapf(err, "unable to fetch domain %s", input.ResourceAttributionId)
}
domainId = tenant.Id
input.DomainId = domainId
input.ResourceAttributionId = tenant.Id
input.ResourceAttributionName = tenant.Name
}
if input.Scope == sDomain && domainId != userCred.GetProjectDomainId() {
return input, httperrors.NewForbiddenError("domain %s admin can't create subscriber for domain %s", userCred.GetProjectDomainId(), domainId)
}
var checkQuery *sqlchemy.SQuery
input.TopicID = t.GetId()
switch input.Type {
case api.SUBSCRIBER_TYPE_RECEIVER:
reIds, err := sm.validateReceivers(ctx, input.Receivers)
if err != nil {
return input, err
}
input.Receivers = reIds
case api.SUBSCRIBER_TYPE_ROLE:
if input.RoleScope == "" {
input.RoleScope = input.ResourceScope
}
roleCache, err := db.RoleCacheManager.FetchRoleByIdOrName(ctx, input.Role)
if err != nil {
return input, errors.Wrapf(err, "unable find role %q", input.Role)
}
input.Role = roleCache.GetId()
checkQuery = sm.Query().Equals("topic_id", input.TopicID).Equals("type", api.SUBSCRIBER_TYPE_ROLE).Equals("resource_scope", input.ResourceScope).Equals("identification", input.Role).Equals("role_scope", input.RoleScope)
case api.SUBSCRIBER_TYPE_ROBOT:
robot, err := RobotManager.FetchByIdOrName(userCred, input.Robot)
if errors.Cause(err) == sql.ErrNoRows {
return input, httperrors.NewInputParameterError("robot %q not found", input.Robot)
}
if err != nil {
return input, errors.Wrapf(err, "unable to fetch robot %q", input.Robot)
}
input.Robot = robot.GetId()
checkQuery = sm.Query().Equals("type", api.SUBSCRIBER_TYPE_ROLE).Equals("topic_id", input.TopicID).Equals("resource_scope", input.ResourceScope).Equals("identification", input.Robot)
default:
return input, httperrors.NewInputParameterError("unkown type %q", input.Type)
}
// check type+resourceScope+identification
if checkQuery != nil {
count, err := checkQuery.CountWithError()
if err != nil {
return input, errors.Wrap(err, "unable to count")
}
if count > 0 {
return input, httperrors.NewForbiddenError("repeated with existing subscribers")
}
}
return input, nil
}
func (s *SSubscriber) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
s.SStandaloneAnonResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
var input api.SubscriberCreateInput
_ = data.Unmarshal(&input)
if s.Type == api.SUBSCRIBER_TYPE_RECEIVER {
err := s.SetReceivers(ctx, input.Receivers)
if err != nil {
logclient.AddActionLogWithContext(ctx, s, logclient.ACT_CREATE, err.Error(), userCred, false)
_, err := db.Update(s, func() error {
s.SetEnabled(false)
return nil
})
if err != nil {
log.Errorf("unable to enable subscriber: %v", err)
}
}
}
logclient.AddActionLogWithContext(ctx, s, logclient.ACT_CREATE, "", userCred, true)
return
}
func (s *SSubscriber) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
err := s.SStandaloneAnonResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data)
if err != nil {
return errors.Wrap(err, "SVirtualResourceBase.CustomizeCreate")
}
var input api.SubscriberCreateInput
_ = data.Unmarshal(&input)
switch input.Type {
case api.SUBSCRIBER_TYPE_RECEIVER:
case api.SUBSCRIBER_TYPE_ROBOT:
s.Identification = input.Robot
case api.SUBSCRIBER_TYPE_ROLE:
s.Identification = input.Role
}
s.Enabled = tristate.True
return nil
}
func (s *SSubscriber) AllowPerformChange(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
return true
}
func (s *SSubscriber) PerformChange(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.SubscriberChangeInput) (jsonutils.JSONObject, error) {
if s.Scope == string(rbacutils.ScopeSystem) {
if !db.IsAdminAllowUpdate(userCred, s) {
return nil, httperrors.NewForbiddenError("")
}
} else {
if !db.IsDomainAllowUpdate(userCred, s) {
return nil, httperrors.NewForbiddenError("")
}
if s.DomainId != userCred.GetProjectDomainId() {
return nil, httperrors.NewForbiddenError("")
}
}
switch s.Type {
case api.SUBSCRIBER_TYPE_RECEIVER:
err := s.SetReceivers(ctx, input.Receivers)
if err != nil {
log.Errorf("unable to set receivers %s", input.Receivers)
}
case api.SUBSCRIBER_TYPE_ROBOT:
_, err := db.Update(s, func() error {
s.Identification = input.Robot
return nil
})
if err != nil {
return nil, errors.Wrap(err, "unable to update subscriber")
}
case api.SUBSCRIBER_TYPE_ROLE:
_, err := db.Update(s, func() error {
s.Identification = input.Role
if input.RoleScope != "" {
s.RoleScope = input.RoleScope
}
return nil
})
if err != nil {
return nil, errors.Wrap(err, "unable to update subscriber")
}
}
return nil, nil
}
func (sm *SSubscriberManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, input api.SubscriberListInput) (*sqlchemy.SQuery, error) {
var err error
q, err = sm.SStandaloneAnonResourceBaseManager.ListItemFilter(ctx, q, userCred, input.StandaloneAnonResourceListInput)
if err != nil {
return nil, errors.Wrap(err, "SVirtualResourceBaseManager.ListItemFilter")
}
q, err = sm.SEnabledResourceBaseManager.ListItemFilter(ctx, q, userCred, input.EnabledResourceBaseListInput)
if err != nil {
return nil, errors.Wrap(err, "SEnabledResourceBaseManager.ListItemFilter")
}
sSystem, sDomain := string(rbacutils.ScopeSystem), string(rbacutils.ScopeDomain)
if input.Scope == "" {
input.Scope = sSystem
}
switch input.Scope {
case sSystem:
allow := db.IsAdminAllowList(userCred, sm)
if !allow {
return nil, httperrors.NewForbiddenError("")
}
case sDomain:
allow := db.IsDomainAllowList(userCred, sm)
if !allow {
return nil, httperrors.NewForbiddenError("")
}
q = q.Equals("domain_id", userCred.GetProjectDomainId())
default:
return nil, httperrors.NewInputParameterError("unkown scope %s", input.Scope)
}
if input.TopicID != "" {
q = q.Equals("topic_id", input.TopicID)
}
if input.Type != "" {
q = q.Equals("type", input.Type)
}
if input.ResourceScope != "" {
q = q.Equals("resource_scope", input.ResourceScope)
}
return q, nil
}
func (sm *SSubscriberManager) FetchCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, objs []interface{}, fields stringutils2.SSortedStrings, isList bool) []api.SubscriberDetails {
var err error
vRows := sm.SStandaloneAnonResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
rows := make([]api.SubscriberDetails, len(objs))
for i := range rows {
rows[i].StandaloneAnonResourceDetails = vRows[i]
s := objs[i].(*SSubscriber)
switch s.Type {
case api.SUBSCRIBER_TYPE_RECEIVER:
rows[i].Receivers, err = s.receiverIdentifications()
if err != nil {
log.Errorf("unable to get receiverIdentifications for subscriber %q: %v", s.Id, err)
}
case api.SUBSCRIBER_TYPE_ROBOT:
rows[i].Robot, err = s.robotIdentification()
if err != nil {
log.Errorf("unable get robotIdentification for subscriber %q: %v", s.Id, err)
}
case api.SUBSCRIBER_TYPE_ROLE:
rows[i].Role, err = s.roleIdentification(ctx)
if err != nil {
log.Errorf("unable to get roleIdentification for subscriber %q: %v", s.Id, err)
}
}
}
return rows
}
func (s *SSubscriber) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
err := s.SStandaloneAnonResourceBase.CustomizeDelete(ctx, userCred, query, data)
if err != nil {
return err
}
if s.Scope == string(rbacutils.ScopeSystem) {
if !db.IsAdminAllowDelete(userCred, s) {
return httperrors.NewForbiddenError("")
}
} else {
if !db.IsDomainAllowDelete(userCred, s) {
return httperrors.NewForbiddenError("")
}
if s.DomainId != userCred.GetProjectDomainId() {
return httperrors.NewForbiddenError("")
}
}
return nil
}
func (s *SSubscriber) receiverIdentifications() ([]api.Identification, error) {
srSubq := SubscriberReceiverManager.Query().Equals("subscriber_id", s.Id).SubQuery()
rq := ReceiverManager.Query("id", "name")
rq = rq.Join(srSubq, sqlchemy.Equals(srSubq.Field("receiver_id"), rq.Field("id")))
var ret []api.Identification
err := rq.All(&ret)
if err != nil {
return nil, err
}
return ret, nil
}
func (s *SSubscriber) robotIdentification() (api.Identification, error) {
var ret api.Identification
q := RobotManager.Query("id", "name").Equals("id", s.Identification)
err := q.First(&ret)
if err != nil {
return ret, err
}
return ret, nil
}
func (s *SSubscriber) roleIdentification(ctx context.Context) (api.Identification, error) {
var ret api.Identification
roleCache, err := db.RoleCacheManager.FetchRoleById(ctx, s.Identification)
if err != nil {
return ret, errors.Wrapf(err, "unable to find role %q", s.Identification)
}
ret.ID = s.Identification
ret.Name = roleCache.Name
return ret, nil
}
func (srm *SSubscriberManager) robot(tid, projectDomainId, projectId string) ([]string, error) {
srs, err := srm.findSuitableOnes(tid, projectDomainId, projectId, api.SUBSCRIBER_TYPE_ROBOT)
if err != nil {
return nil, err
}
robotIds := make([]string, len(srs))
for i := range srs {
robotIds[i] = srs[i].Identification
}
return robotIds, nil
}
func (srm *SSubscriberManager) findSuitableOnes(tid, projectDomainId, projectId string, types ...string) ([]SSubscriber, error) {
q := srm.Query().Equals("topic_id", tid).IsTrue("enabled")
q = q.Filter(sqlchemy.OR(
sqlchemy.AND(
sqlchemy.Equals(q.Field("resource_scope"), api.SUBSCRIBER_SCOPE_PROJECT),
sqlchemy.Equals(q.Field("resource_attribution_id"), projectId),
),
sqlchemy.AND(
sqlchemy.Equals(q.Field("resource_scope"), api.SUBSCRIBER_SCOPE_DOMAIN),
sqlchemy.Equals(q.Field("resource_attribution_id"), projectDomainId),
),
sqlchemy.Equals(q.Field("resource_scope"), api.SUBSCRIBER_SCOPE_SYSTEM),
))
switch len(types) {
case 0:
case 1:
q = q.Equals("type", types[0])
default:
q = q.In("type", types)
}
srs := make([]SSubscriber, 0, 1)
err := db.FetchModelObjects(srm, q, &srs)
if err != nil {
return nil, err
}
return srs, nil
}
// TODO: Use cache to increase speed
func (srm *SSubscriberManager) getReceiversSent(ctx context.Context, tid string, projectDomainId string, projectId string) ([]string, error) {
srs, err := srm.findSuitableOnes(tid, projectDomainId, projectId, api.SUBSCRIBER_TYPE_RECEIVER, api.SUBSCRIBER_TYPE_ROLE)
if err != nil {
return nil, err
}
receivers := make([]string, 0, len(srs))
roleMap := make(map[string][]string, 3)
receivermap := make(map[string]*[]string, 3)
for _, sr := range srs {
if sr.Type == api.SUBSCRIBER_TYPE_RECEIVER {
rIds, err := sr.getReceivers()
if err != nil {
return nil, errors.Wrap(err, "unable to get receivers")
}
receivers = append(receivers, rIds...)
} else if sr.Type == api.SUBSCRIBER_TYPE_ROLE {
roleMap[sr.RoleScope] = append(roleMap[sr.RoleScope], sr.Identification)
receivermap[sr.RoleScope] = &[]string{}
}
}
errgo, _ := errgroup.WithContext(ctx)
for _scope, _roles := range roleMap {
scope, roles := _scope, _roles
receivers := receivermap[scope]
errgo.Go(func() error {
query := jsonutils.NewDict()
query.Set("roles", jsonutils.NewStringArray(roles))
query.Set("effective", jsonutils.JSONTrue)
switch scope {
case api.SUBSCRIBER_SCOPE_SYSTEM:
case api.SUBSCRIBER_SCOPE_DOMAIN:
if len(projectDomainId) == 0 {
return fmt.Errorf("need projectDomainId")
}
query.Set("project_domain_id", jsonutils.NewString(projectDomainId))
case api.SUBSCRIBER_SCOPE_PROJECT:
if len(projectId) == 0 {
return fmt.Errorf("need projectId")
}
query.Add(jsonutils.NewString(projectId), "scope", "project", "id")
}
s := auth.GetAdminSession(ctx, "", "")
log.Debugf("query for role-assignments: %s", query.String())
listRet, err := modules.RoleAssignments.List(s, query)
if err != nil {
return errors.Wrap(err, "unable to list RoleAssignments")
}
log.Debugf("return value for role-assignments: %s", jsonutils.Marshal(listRet))
for i := range listRet.Data {
ras := listRet.Data[i]
user, err := ras.Get("user")
if err == nil {
id, err := user.GetString("id")
if err != nil {
return errors.Wrap(err, "unable to get user.id from result of RoleAssignments.List")
}
*receivers = append(*receivers, id)
}
}
return nil
})
}
err = errgo.Wait()
if err != nil {
return nil, err
}
for _, res := range receivermap {
receivers = append(receivers, *res...)
}
// de-duplication
return sets.NewString(receivers...).UnsortedList(), nil
}
func (sr *SSubscriber) getReceivers() ([]string, error) {
srrs, err := SubscriberReceiverManager.getBySubscriberId(sr.Id)
if err != nil {
return nil, err
}
rIds := make([]string, len(srrs))
for i := range srrs {
rIds[i] = srrs[i].ReceiverId
}
return rIds, nil
}
func (sr *SSubscriber) SetReceivers(ctx context.Context, receiverIds []string) error {
srrs, err := SubscriberReceiverManager.getBySubscriberId(sr.Id)
if err != nil {
return errors.Wrapf(err, "unable to get SRReceiver by Subscriber %s", sr.Id)
}
dbReceivers := make([]string, len(srrs))
for i := range srrs {
dbReceivers[i] = srrs[i].ReceiverId
}
var addReceivers, rmReceivers []string
sort.Strings(dbReceivers)
sort.Strings(receiverIds)
for i, j := 0, 0; i < len(dbReceivers) || j < len(receiverIds); {
switch {
case i == len(dbReceivers):
addReceivers = append(addReceivers, receiverIds[j])
j++
case j == len(receiverIds):
rmReceivers = append(rmReceivers, dbReceivers[i])
i++
case dbReceivers[i] > receiverIds[j]:
addReceivers = append(addReceivers, receiverIds[j])
j++
case dbReceivers[i] < receiverIds[j]:
rmReceivers = append(rmReceivers, dbReceivers[i])
i++
case dbReceivers[i] == receiverIds[j]:
i++
j++
}
}
// add
for _, rId := range addReceivers {
_, err := SubscriberReceiverManager.create(ctx, sr.Id, rId)
if err != nil {
return errors.Wrapf(err, "unable to connect subscription receiver %q with receiver %q", sr.Id, rId)
}
}
for _, rId := range rmReceivers {
err := SubscriberReceiverManager.delete(sr.Id, rId)
if err != nil {
return errors.Wrapf(err, "unable to disconnect subscription receiver %q with receiver %q", sr.Id, rId)
}
}
return nil
}
func (s *SSubscriber) AllowPerformSetReceiver(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, s, "set-receiver")
}
func (s *SSubscriber) PerformSetReceiver(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.SubscriberSetReceiverInput) (jsonutils.JSONObject, error) {
reIds, err := SubscriberManager.validateReceivers(ctx, input.Receivers)
if err != nil {
return nil, err
}
return nil, s.SetReceivers(ctx, reIds)
}
func (s *SSubscriber) AllowPerformEnable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, s, "enable")
}
func (s *SSubscriber) PerformEnable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input jsonutils.JSONObject) (jsonutils.JSONObject, error) {
err := db.EnabledPerformEnable(s, ctx, userCred, true)
if err != nil {
return nil, errors.Wrap(err, "EnabledPerformEnable")
}
return nil, nil
}
func (s *SSubscriber) AllowPerformDisable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input jsonutils.JSONObject) bool {
return db.IsAdminAllowPerform(userCred, s, "disable")
}
func (s *SSubscriber) PerformDisable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input jsonutils.JSONObject) (jsonutils.JSONObject, error) {
err := db.EnabledPerformEnable(s, ctx, userCred, false)
if err != nil {
return nil, errors.Wrap(err, "EnabledPerformEnable")
}
return nil, nil
}
func (sm *SSubscriberManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
q, err := sm.SStandaloneAnonResourceBaseManager.QueryDistinctExtraField(q, field)
if err == nil {
return q, nil
}
switch field {
case "resource_scope":
return sm.Query("resource_scope").Distinct(), nil
case "type":
return sm.Query("type").Distinct(), nil
}
return q, nil
}