cloudpolicy.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package models
  15. import (
  16. "context"
  17. "database/sql"
  18. "yunion.io/x/cloudmux/pkg/cloudprovider"
  19. "yunion.io/x/jsonutils"
  20. "yunion.io/x/log"
  21. "yunion.io/x/pkg/errors"
  22. "yunion.io/x/pkg/gotypes"
  23. "yunion.io/x/pkg/util/compare"
  24. "yunion.io/x/sqlchemy"
  25. "yunion.io/x/onecloud/pkg/apis"
  26. api "yunion.io/x/onecloud/pkg/apis/cloudid"
  27. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  28. "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
  29. "yunion.io/x/onecloud/pkg/httperrors"
  30. "yunion.io/x/onecloud/pkg/mcclient"
  31. "yunion.io/x/onecloud/pkg/util/stringutils2"
  32. )
  33. type SCloudpolicyManager struct {
  34. db.SStatusInfrasResourceBaseManager
  35. db.SExternalizedResourceBaseManager
  36. SCloudaccountResourceBaseManager
  37. SCloudproviderResourceBaseManager
  38. }
  39. var CloudpolicyManager *SCloudpolicyManager
  40. func init() {
  41. CloudpolicyManager = &SCloudpolicyManager{
  42. SStatusInfrasResourceBaseManager: db.NewStatusInfrasResourceBaseManager(
  43. SCloudpolicy{},
  44. "cloudpolicy_tbl",
  45. "cloudpolicy",
  46. "cloudpolicies",
  47. ),
  48. }
  49. CloudpolicyManager.SetVirtualObject(CloudpolicyManager)
  50. }
  51. type SCloudpolicy struct {
  52. db.SStatusInfrasResourceBase
  53. db.SExternalizedResourceBase
  54. SCloudaccountResourceBase
  55. SCloudproviderResourceBase
  56. // 权限类型
  57. //
  58. // | 权限类型 | 说明 |
  59. // |---------------|----------------------|
  60. // | system | 平台内置权限 |
  61. // | custom | 用户自定义权限 |
  62. PolicyType string `width:"16" charset:"ascii" list:"domain" create:"optional" default:"custom"`
  63. // 策略内容
  64. Document *jsonutils.JSONDict `length:"long" charset:"utf8" list:"domain" update:"domain" create:"domain_required"`
  65. }
  66. func (self SCloudpolicy) GetGlobalId() string {
  67. return self.ExternalId
  68. }
  69. func (manager *SCloudpolicyManager) GetIVirtualModelManager() db.IVirtualModelManager {
  70. return manager.GetVirtualObject().(db.IVirtualModelManager)
  71. }
  72. func (manager *SCloudpolicyManager) GetResourceCount() ([]db.SScopeResourceCount, error) {
  73. return nil, nil
  74. }
  75. func (self *SCloudpolicy) GetCloudprovider() (*SCloudprovider, error) {
  76. provider, err := CloudproviderManager.FetchById(self.ManagerId)
  77. if err != nil {
  78. return nil, errors.Wrapf(err, "CloudproviderManager.FetchById(%s)", self.ManagerId)
  79. }
  80. return provider.(*SCloudprovider), nil
  81. }
  82. func (self *SCloudpolicy) GetProvider() (cloudprovider.ICloudProvider, error) {
  83. if len(self.ManagerId) > 0 {
  84. provider, err := self.GetCloudprovider()
  85. if err != nil {
  86. return nil, errors.Wrap(err, "GetCloudprovider")
  87. }
  88. return provider.GetProvider()
  89. }
  90. account, err := self.GetCloudaccount()
  91. if err != nil {
  92. return nil, errors.Wrap(err, "GetCloudaccount")
  93. }
  94. return account.GetProvider()
  95. }
  96. // 公有云权限列表
  97. func (manager *SCloudpolicyManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query api.CloudpolicyListInput) (*sqlchemy.SQuery, error) {
  98. var err error
  99. q, err = manager.SStatusInfrasResourceBaseManager.ListItemFilter(ctx, q, userCred, query.StatusInfrasResourceBaseListInput)
  100. if err != nil {
  101. return nil, err
  102. }
  103. q, err = manager.SCloudaccountResourceBaseManager.ListItemFilter(ctx, q, userCred, query.CloudaccountResourceListInput)
  104. if err != nil {
  105. return nil, err
  106. }
  107. q, err = manager.SCloudproviderResourceBaseManager.ListItemFilter(ctx, q, userCred, query.CloudproviderResourceListInput)
  108. if err != nil {
  109. return nil, err
  110. }
  111. if len(query.PolicyType) > 0 {
  112. q = q.Equals("policy_type", query.PolicyType)
  113. }
  114. if len(query.ClouduserId) > 0 {
  115. _, err = ClouduserManager.FetchById(query.ClouduserId)
  116. if err != nil {
  117. if errors.Cause(err) == sql.ErrNoRows {
  118. return nil, httperrors.NewResourceNotFoundError2("clouduser", query.ClouduserId)
  119. }
  120. return q, httperrors.NewGeneralError(errors.Wrap(err, "ClouduserManager.FetchById"))
  121. }
  122. sq := ClouduserPolicyManager.Query("cloudpolicy_id").Equals("clouduser_id", query.ClouduserId)
  123. q = q.In("id", sq.SubQuery())
  124. }
  125. if len(query.CloudgroupId) > 0 {
  126. _, err = CloudgroupManager.FetchById(query.CloudgroupId)
  127. if err != nil {
  128. if errors.Cause(err) == sql.ErrNoRows {
  129. return nil, httperrors.NewResourceNotFoundError2("cloudgroup", query.CloudgroupId)
  130. }
  131. return q, httperrors.NewGeneralError(errors.Wrap(err, "CloudgroupManager.FetchById"))
  132. }
  133. sq := CloudgroupPolicyManager.Query("cloudpolicy_id").Equals("cloudgroup_id", query.CloudgroupId)
  134. q = q.In("id", sq.SubQuery())
  135. }
  136. return q, nil
  137. }
  138. // +onecloud:swagger-gen-ignore
  139. func (manager *SCloudpolicyManager) ValidateCreateData(
  140. ctx context.Context,
  141. userCred mcclient.TokenCredential,
  142. ownerId mcclient.IIdentityProvider,
  143. query jsonutils.JSONObject,
  144. input *api.CloudpolicyCreateInput,
  145. ) (*api.CloudpolicyCreateInput, error) {
  146. return nil, cloudprovider.ErrNotImplemented
  147. }
  148. // +onecloud:swagger-gen-ignore
  149. func (self *SCloudpolicy) ValidateUpdateData(
  150. ctx context.Context,
  151. userCred mcclient.TokenCredential,
  152. query jsonutils.JSONObject,
  153. input *api.CloudpolicyUpdateInput,
  154. ) (*api.CloudpolicyUpdateInput, error) {
  155. return nil, cloudprovider.ErrNotSupported
  156. }
  157. func (self *SCloudpolicy) ValidateDeleteCondition(ctx context.Context, info *api.CloudpolicyDetails) error {
  158. if self.PolicyType == api.CLOUD_POLICY_TYPE_SYSTEM {
  159. return httperrors.NewNotSupportedError("can not delete system policy")
  160. }
  161. if gotypes.IsNil(info) {
  162. info := &api.CloudpolicyDetails{}
  163. usage, err := CloudpolicyManager.TotalResourceCount([]string{self.Id})
  164. if err != nil {
  165. return err
  166. }
  167. info.PolicyUsage, _ = usage[self.Id]
  168. }
  169. if info.CloudgroupCount > 0 || info.ClouduserCount > 0 {
  170. return httperrors.NewNotEmptyError("attach %d groups, %d users", info.CloudgroupCount, info.ClouduserCount)
  171. }
  172. return self.SStatusInfrasResourceBase.ValidateDeleteCondition(ctx, nil)
  173. }
  174. func (manager *SCloudpolicyManager) OrderByExtraFields(
  175. ctx context.Context,
  176. q *sqlchemy.SQuery,
  177. userCred mcclient.TokenCredential,
  178. query api.CloudpolicyListInput,
  179. ) (*sqlchemy.SQuery, error) {
  180. var err error
  181. q, err = manager.SStatusInfrasResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.StatusInfrasResourceBaseListInput)
  182. if err != nil {
  183. return nil, errors.Wrap(err, "SStatusStandaloneResourceBaseManager.OrderByExtraFields")
  184. }
  185. return q, nil
  186. }
  187. func (manager *SCloudpolicyManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
  188. var err error
  189. q, err = manager.SStatusInfrasResourceBaseManager.QueryDistinctExtraField(q, field)
  190. if err == nil {
  191. return q, nil
  192. }
  193. return q, httperrors.ErrNotFound
  194. }
  195. type SPolicyUsageCount struct {
  196. Id string
  197. api.PolicyUsage
  198. }
  199. func (m *SCloudpolicyManager) query(manager db.IModelManager, field string, policyIds []string, filter func(*sqlchemy.SQuery) *sqlchemy.SQuery) *sqlchemy.SSubQuery {
  200. q := manager.Query()
  201. if filter != nil {
  202. q = filter(q)
  203. }
  204. sq := q.SubQuery()
  205. return sq.Query(
  206. sq.Field("cloudpolicy_id"),
  207. sqlchemy.COUNT(field),
  208. ).In("cloudpolicy_id", policyIds).GroupBy(sq.Field("cloudpolicy_id")).SubQuery()
  209. }
  210. func (manager *SCloudpolicyManager) TotalResourceCount(policyIds []string) (map[string]api.PolicyUsage, error) {
  211. // group
  212. groupSQ := manager.query(CloudgroupPolicyManager, "group_cnt", policyIds, nil)
  213. userSQ := manager.query(ClouduserPolicyManager, "user_cnt", policyIds, nil)
  214. policy := manager.Query().SubQuery()
  215. policyQ := policy.Query(
  216. sqlchemy.SUM("cloudgroup_count", groupSQ.Field("group_cnt")),
  217. sqlchemy.SUM("clouduser_count", userSQ.Field("user_cnt")),
  218. )
  219. policyQ.AppendField(policyQ.Field("id"))
  220. policyQ = policyQ.LeftJoin(groupSQ, sqlchemy.Equals(policyQ.Field("id"), groupSQ.Field("cloudpolicy_id")))
  221. policyQ = policyQ.LeftJoin(userSQ, sqlchemy.Equals(policyQ.Field("id"), userSQ.Field("cloudpolicy_id")))
  222. policyQ = policyQ.Filter(sqlchemy.In(policyQ.Field("id"), policyIds)).GroupBy(policyQ.Field("id"))
  223. policyCount := []SPolicyUsageCount{}
  224. err := policyQ.All(&policyCount)
  225. if err != nil {
  226. return nil, errors.Wrapf(err, "policyQ.All")
  227. }
  228. result := map[string]api.PolicyUsage{}
  229. for i := range policyCount {
  230. result[policyCount[i].Id] = policyCount[i].PolicyUsage
  231. }
  232. return result, nil
  233. }
  234. // 获取公有云权限详情
  235. func (manager *SCloudpolicyManager) FetchCustomizeColumns(
  236. ctx context.Context,
  237. userCred mcclient.TokenCredential,
  238. query jsonutils.JSONObject,
  239. objs []interface{},
  240. fields stringutils2.SSortedStrings,
  241. isList bool,
  242. ) []api.CloudpolicyDetails {
  243. rows := make([]api.CloudpolicyDetails, len(objs))
  244. infsRows := manager.SStatusInfrasResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  245. acRows := manager.SCloudaccountResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  246. mRows := manager.SCloudproviderResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
  247. policyIds := make([]string, len(objs))
  248. for i := range rows {
  249. rows[i] = api.CloudpolicyDetails{
  250. StatusInfrasResourceBaseDetails: infsRows[i],
  251. CloudaccountResourceDetails: acRows[i],
  252. CloudproviderResourceDetails: mRows[i],
  253. }
  254. policy := objs[i].(*SCloudpolicy)
  255. policyIds[i] = policy.Id
  256. }
  257. usage, err := manager.TotalResourceCount(policyIds)
  258. if err != nil {
  259. log.Errorf("TotalResourceCount error: %v", err)
  260. return rows
  261. }
  262. for i := range rows {
  263. rows[i].PolicyUsage, _ = usage[policyIds[i]]
  264. }
  265. return rows
  266. }
  267. func (self *SCloudpolicy) SyncWithCloudpolicy(ctx context.Context, userCred mcclient.TokenCredential, iPolicy cloudprovider.ICloudpolicy) error {
  268. _, err := db.Update(self, func() error {
  269. self.Name = iPolicy.GetName()
  270. if self.PolicyType == api.CLOUD_POLICY_TYPE_CUSTOM || len(self.Description) == 0 {
  271. self.Description = iPolicy.GetDescription()
  272. }
  273. self.Status = apis.STATUS_AVAILABLE
  274. self.IsPublic = true
  275. if self.PolicyType == api.CLOUD_POLICY_TYPE_CUSTOM || gotypes.IsNil(self.Document) {
  276. doc, err := iPolicy.GetDocument()
  277. if err != nil {
  278. return errors.Wrapf(err, "GetDocument")
  279. }
  280. self.Document = doc
  281. }
  282. return nil
  283. })
  284. if err != nil {
  285. return errors.Wrap(err, "db.Update")
  286. }
  287. return nil
  288. }
  289. func (self *SCloudaccount) newCloudpolicy(ctx context.Context, userCred mcclient.TokenCredential, iPolicy cloudprovider.ICloudpolicy, managerId string) (*SCloudpolicy, error) {
  290. policy := &SCloudpolicy{}
  291. policy.SetModelManager(CloudpolicyManager, policy)
  292. doc, err := iPolicy.GetDocument()
  293. if err != nil {
  294. return nil, err
  295. }
  296. policy.Document = doc
  297. policy.Name = iPolicy.GetName()
  298. policy.Status = apis.STATUS_AVAILABLE
  299. policy.PolicyType = string(iPolicy.GetPolicyType())
  300. policy.IsPublic = true
  301. policy.ExternalId = iPolicy.GetGlobalId()
  302. policy.Description = iPolicy.GetDescription()
  303. policy.CloudaccountId = self.Id
  304. policy.ManagerId = managerId
  305. return policy, CloudpolicyManager.TableSpec().Insert(ctx, policy)
  306. }
  307. func (self *SCloudaccount) SyncPolicies(ctx context.Context, userCred mcclient.TokenCredential, iPolicies []cloudprovider.ICloudpolicy, managerId string) compare.SyncResult {
  308. lockman.LockRawObject(ctx, CloudproviderManager.Keyword(), managerId)
  309. defer lockman.ReleaseRawObject(ctx, CloudproviderManager.Keyword(), managerId)
  310. result := compare.SyncResult{}
  311. removed := make([]SCloudpolicy, 0)
  312. commondb := make([]SCloudpolicy, 0)
  313. commonext := make([]cloudprovider.ICloudpolicy, 0)
  314. added := make([]cloudprovider.ICloudpolicy, 0)
  315. dbPolicies, err := self.GetCloudpolicies(managerId)
  316. if err != nil {
  317. result.Error(errors.Wrapf(err, "GetCloudpolicies"))
  318. return result
  319. }
  320. err = compare.CompareSets(dbPolicies, iPolicies, &removed, &commondb, &commonext, &added)
  321. if err != nil {
  322. result.Error(errors.Wrapf(err, "compare.CompareSets"))
  323. return result
  324. }
  325. for i := 0; i < len(removed); i++ {
  326. err = removed[i].Delete(ctx, userCred)
  327. if err != nil {
  328. result.DeleteError(err)
  329. continue
  330. }
  331. result.Delete()
  332. }
  333. for i := 0; i < len(commondb); i++ {
  334. err = commondb[i].SyncWithCloudpolicy(ctx, userCred, commonext[i])
  335. if err != nil {
  336. result.UpdateError(err)
  337. continue
  338. }
  339. result.Update()
  340. }
  341. for i := 0; i < len(added); i++ {
  342. _, err := self.newCloudpolicy(ctx, userCred, added[i], managerId)
  343. if err != nil {
  344. result.AddError(err)
  345. continue
  346. }
  347. result.Add()
  348. }
  349. return result
  350. }