sharablebase.go 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713
  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 db
  15. import (
  16. "context"
  17. "yunion.io/x/jsonutils"
  18. "yunion.io/x/log"
  19. "yunion.io/x/pkg/errors"
  20. "yunion.io/x/pkg/util/rbacscope"
  21. "yunion.io/x/sqlchemy"
  22. "yunion.io/x/onecloud/pkg/apis"
  23. "yunion.io/x/onecloud/pkg/cloudcommon/consts"
  24. "yunion.io/x/onecloud/pkg/cloudcommon/policy"
  25. "yunion.io/x/onecloud/pkg/httperrors"
  26. "yunion.io/x/onecloud/pkg/mcclient"
  27. "yunion.io/x/onecloud/pkg/util/logclient"
  28. "yunion.io/x/onecloud/pkg/util/stringutils2"
  29. "yunion.io/x/onecloud/pkg/util/tagutils"
  30. )
  31. type SSharableBaseResourceManager struct{}
  32. func (manager *SSharableBaseResourceManager) ListItemFilter(
  33. ctx context.Context,
  34. q *sqlchemy.SQuery,
  35. userCred mcclient.TokenCredential,
  36. query apis.SharableResourceBaseListInput,
  37. ) (*sqlchemy.SQuery, error) {
  38. if query.IsPublic != nil {
  39. if *query.IsPublic == true {
  40. q = q.IsTrue("is_public")
  41. } else {
  42. q = q.IsFalse("is_public")
  43. }
  44. }
  45. if len(query.PublicScope) > 0 {
  46. q = q.Equals("public_scope", query.PublicScope)
  47. }
  48. return q, nil
  49. }
  50. func (manager *SSharableBaseResourceManager) FetchCustomizeColumns(
  51. ctx context.Context,
  52. userCred mcclient.TokenCredential,
  53. query jsonutils.JSONObject,
  54. objs []interface{},
  55. fields stringutils2.SSortedStrings,
  56. isList bool,
  57. ) []apis.SharableResourceBaseInfo {
  58. rows := make([]apis.SharableResourceBaseInfo, len(objs))
  59. var resType string
  60. resIds := make([]string, len(rows))
  61. var resScope rbacscope.TRbacScope
  62. for i := range rows {
  63. if model, ok := objs[i].(ISharableBaseModel); ok {
  64. if len(resType) == 0 {
  65. resType = model.Keyword()
  66. }
  67. if len(resScope) == 0 {
  68. resScope = model.GetModelManager().ResourceScope()
  69. }
  70. resIds[i] = model.GetId()
  71. }
  72. }
  73. q := SharedResourceManager.Query()
  74. q = q.Equals("resource_type", resType)
  75. sharedResourceMap := make(map[string][]SSharedResource)
  76. err := FetchQueryObjectsByIds(q, "resource_id", resIds, &sharedResourceMap)
  77. if err != nil {
  78. log.Errorf("FetchQueryObjectsByIds for shared resource fail %s", err)
  79. return rows
  80. }
  81. targetTenantIds := stringutils2.NewSortedStrings([]string{})
  82. targetDomainIds := stringutils2.NewSortedStrings([]string{})
  83. for _, srs := range sharedResourceMap {
  84. for _, sr := range srs {
  85. switch sr.TargetType {
  86. case SharedTargetProject:
  87. targetTenantIds = stringutils2.Append(targetTenantIds, sr.TargetProjectId)
  88. case SharedTargetDomain:
  89. targetDomainIds = stringutils2.Append(targetDomainIds, sr.TargetProjectId)
  90. }
  91. }
  92. }
  93. var tenantMap map[string]STenant
  94. var domainMap map[string]STenant
  95. if len(targetTenantIds) > 0 {
  96. tenantMap = DefaultProjectsFetcher(ctx, targetTenantIds, false)
  97. }
  98. if len(targetDomainIds) > 0 {
  99. domainMap = DefaultProjectsFetcher(ctx, targetDomainIds, true)
  100. }
  101. for i := range rows {
  102. resId := resIds[i]
  103. if srs, ok := sharedResourceMap[resId]; ok {
  104. projects := make([]apis.SharedProject, 0)
  105. domains := make([]apis.SharedDomain, 0)
  106. for _, sr := range srs {
  107. switch sr.TargetType {
  108. case SharedTargetProject:
  109. project := apis.SharedProject{}
  110. project.Id = sr.TargetProjectId
  111. if tenant, ok := tenantMap[sr.TargetProjectId]; ok {
  112. project.Name = tenant.Name
  113. project.Domain = tenant.Domain
  114. project.DomainId = tenant.DomainId
  115. }
  116. projects = append(projects, project)
  117. case SharedTargetDomain:
  118. domain := apis.SharedDomain{}
  119. domain.Id = sr.TargetProjectId
  120. if tenant, ok := domainMap[sr.TargetProjectId]; ok {
  121. domain.Name = tenant.Name
  122. }
  123. domains = append(domains, domain)
  124. }
  125. }
  126. rows[i].SharedProjects = projects
  127. rows[i].SharedDomains = domains
  128. }
  129. }
  130. return rows
  131. }
  132. func SharableManagerValidateCreateData(
  133. manager IStandaloneModelManager,
  134. ctx context.Context,
  135. userCred mcclient.TokenCredential,
  136. ownerId mcclient.IIdentityProvider,
  137. query jsonutils.JSONObject,
  138. input apis.SharableResourceBaseCreateInput,
  139. ) (apis.SharableResourceBaseCreateInput, error) {
  140. resScope := manager.ResourceScope()
  141. reqScope := resScope
  142. isPublic := true
  143. switch resScope {
  144. case rbacscope.ScopeProject:
  145. if input.PublicScope == string(rbacscope.ScopeSystem) {
  146. input.IsPublic = &isPublic
  147. reqScope = rbacscope.ScopeSystem
  148. } else if input.PublicScope == string(rbacscope.ScopeDomain) {
  149. if consts.GetNonDefaultDomainProjects() {
  150. // only if non_default_domain_projects turned on, allow sharing to domain
  151. input.IsPublic = &isPublic
  152. reqScope = rbacscope.ScopeDomain
  153. } else {
  154. input.IsPublic = &isPublic
  155. reqScope = rbacscope.ScopeSystem
  156. }
  157. } else if input.IsPublic != nil && *input.IsPublic && len(input.PublicScope) == 0 {
  158. // backward compatible, if only is_public is true, make it share to system
  159. input.IsPublic = &isPublic
  160. input.PublicScope = string(rbacscope.ScopeSystem)
  161. reqScope = rbacscope.ScopeSystem
  162. } else {
  163. input.IsPublic = nil
  164. input.PublicScope = "" // string(rbacscope.ScopeNone)
  165. }
  166. case rbacscope.ScopeDomain:
  167. if consts.GetNonDefaultDomainProjects() {
  168. // only if non_default_domain_projects turned on, allow sharing domain resources
  169. if input.PublicScope == string(rbacscope.ScopeSystem) {
  170. input.IsPublic = &isPublic
  171. reqScope = rbacscope.ScopeSystem
  172. } else if input.IsPublic != nil && *input.IsPublic && len(input.PublicScope) == 0 {
  173. // backward compatible, if only is_public is true, make it share to system
  174. input.IsPublic = &isPublic
  175. input.PublicScope = string(rbacscope.ScopeSystem)
  176. reqScope = rbacscope.ScopeSystem
  177. } else {
  178. input.IsPublic = nil
  179. input.PublicScope = "" // string(rbacscope.ScopeNone)
  180. }
  181. } else {
  182. // if non_default_domain_projects turned off, all domain resources shared to system
  183. input.IsPublic = &isPublic
  184. input.PublicScope = string(rbacscope.ScopeSystem)
  185. reqScope = rbacscope.ScopeSystem
  186. }
  187. default:
  188. return input, errors.Wrap(httperrors.ErrInputParameter, "the resource is not sharable")
  189. }
  190. if input.IsPublic != nil && *input.IsPublic {
  191. // TODO: deal with policyTags
  192. allowScope, _ := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), manager.KeywordPlural(), policy.PolicyActionPerform, "public")
  193. if reqScope.HigherThan(allowScope) {
  194. return input, errors.Wrapf(httperrors.ErrNotSufficientPrivilege, "require %s allow %s", reqScope, allowScope)
  195. }
  196. }
  197. return input, nil
  198. }
  199. func SharableManagerFilterByOwner(ctx context.Context, manager IStandaloneModelManager, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, owner mcclient.IIdentityProvider, scope rbacscope.TRbacScope) *sqlchemy.SQuery {
  200. if owner != nil {
  201. resScope := manager.ResourceScope()
  202. if resScope == rbacscope.ScopeUser {
  203. targetProjectId := owner.GetProjectId()
  204. if len(targetProjectId) == 0 && userCred != nil {
  205. targetProjectId = userCred.GetProjectId()
  206. }
  207. subq := SharedResourceManager.Query("resource_id")
  208. subq = subq.Equals("resource_type", manager.Keyword())
  209. subq = subq.Equals("target_project_id", targetProjectId)
  210. subq = subq.Equals("target_type", SharedTargetProject)
  211. subq2 := SharedResourceManager.Query("resource_id")
  212. subq2 = subq2.Equals("resource_type", manager.Keyword())
  213. subq2 = subq2.Equals("target_project_id", owner.GetProjectDomainId())
  214. subq2 = subq2.Equals("target_type", SharedTargetDomain)
  215. filters := []sqlchemy.ICondition{
  216. sqlchemy.AND(
  217. sqlchemy.IsTrue(q.Field("is_public")),
  218. sqlchemy.Equals(q.Field("public_scope"), rbacscope.ScopeSystem),
  219. ),
  220. sqlchemy.AND(
  221. sqlchemy.IsTrue(q.Field("is_public")),
  222. sqlchemy.Equals(q.Field("public_scope"), rbacscope.ScopeDomain),
  223. sqlchemy.OR(
  224. sqlchemy.In(q.Field("id"), subq2.SubQuery()),
  225. ),
  226. ),
  227. sqlchemy.In(q.Field("id"), subq.SubQuery()),
  228. }
  229. ownerUserId := owner.GetUserId()
  230. if len(ownerUserId) > 0 {
  231. filters = append(filters, sqlchemy.Equals(q.Field("owner_id"), ownerUserId))
  232. }
  233. q = q.Filter(sqlchemy.OR(filters...))
  234. if userCred != nil {
  235. result := policy.PolicyManager.Allow(scope, userCred, consts.GetServiceType(), manager.KeywordPlural(), policy.PolicyActionList)
  236. if !result.ObjectTags.IsEmpty() {
  237. policyTagFilters := tagutils.STagFilters{}
  238. policyTagFilters.AddFilters(result.ObjectTags)
  239. q = ObjectIdQueryWithTagFilters(ctx, q, "id", manager.Keyword(), policyTagFilters)
  240. }
  241. }
  242. } else if resScope == rbacscope.ScopeProject && scope == rbacscope.ScopeProject {
  243. ownerProjectId := owner.GetProjectId()
  244. if len(ownerProjectId) > 0 {
  245. subq := SharedResourceManager.Query("resource_id")
  246. subq = subq.Equals("resource_type", manager.Keyword())
  247. subq = subq.Equals("target_project_id", ownerProjectId)
  248. subq = subq.Equals("target_type", SharedTargetProject)
  249. subq2 := SharedResourceManager.Query("resource_id")
  250. subq2 = subq2.Equals("resource_type", manager.Keyword())
  251. subq2 = subq2.Equals("target_project_id", owner.GetProjectDomainId())
  252. subq2 = subq2.Equals("target_type", SharedTargetDomain)
  253. q = q.Filter(sqlchemy.OR(
  254. sqlchemy.Equals(q.Field("tenant_id"), ownerProjectId),
  255. sqlchemy.AND(
  256. sqlchemy.IsTrue(q.Field("is_public")),
  257. sqlchemy.Equals(q.Field("public_scope"), rbacscope.ScopeSystem),
  258. ),
  259. sqlchemy.AND(
  260. sqlchemy.IsTrue(q.Field("is_public")),
  261. sqlchemy.Equals(q.Field("public_scope"), rbacscope.ScopeDomain),
  262. sqlchemy.OR(
  263. sqlchemy.Equals(q.Field("domain_id"), owner.GetProjectDomainId()),
  264. sqlchemy.In(q.Field("id"), subq2.SubQuery()),
  265. ),
  266. ),
  267. sqlchemy.In(q.Field("id"), subq.SubQuery()),
  268. ))
  269. if userCred != nil {
  270. result := policy.PolicyManager.Allow(scope, userCred, consts.GetServiceType(), manager.KeywordPlural(), policy.PolicyActionList)
  271. if !result.ObjectTags.IsEmpty() {
  272. policyTagFilters := tagutils.STagFilters{}
  273. policyTagFilters.AddFilters(result.ObjectTags)
  274. q = ObjectIdQueryWithTagFilters(ctx, q, "id", manager.Keyword(), policyTagFilters)
  275. }
  276. }
  277. }
  278. } else if (resScope == rbacscope.ScopeDomain && (scope == rbacscope.ScopeProject || scope == rbacscope.ScopeDomain)) || (resScope == rbacscope.ScopeProject && scope == rbacscope.ScopeDomain) {
  279. // domain view
  280. ownerDomainId := owner.GetProjectDomainId()
  281. if len(ownerDomainId) > 0 {
  282. subq := SharedResourceManager.Query("resource_id")
  283. subq = subq.Equals("resource_type", manager.Keyword())
  284. subq = subq.Equals("target_project_id", ownerDomainId)
  285. subq = subq.Equals("target_type", SharedTargetDomain)
  286. q = q.Filter(sqlchemy.OR(
  287. sqlchemy.Equals(q.Field("domain_id"), ownerDomainId),
  288. sqlchemy.AND(
  289. sqlchemy.IsTrue(q.Field("is_public")),
  290. sqlchemy.Equals(q.Field("public_scope"), rbacscope.ScopeSystem),
  291. ),
  292. sqlchemy.AND(
  293. sqlchemy.IsTrue(q.Field("is_public")),
  294. sqlchemy.In(q.Field("id"), subq.SubQuery()),
  295. ),
  296. ))
  297. if userCred != nil {
  298. result := policy.PolicyManager.Allow(scope, userCred, consts.GetServiceType(), manager.KeywordPlural(), policy.PolicyActionList)
  299. if !result.ProjectTags.IsEmpty() && resScope == rbacscope.ScopeProject {
  300. policyTagFilters := tagutils.STagFilters{}
  301. policyTagFilters.AddFilters(result.ProjectTags)
  302. q = ObjectIdQueryWithTagFilters(ctx, q, "tenant_id", "project", policyTagFilters)
  303. }
  304. if !result.ObjectTags.IsEmpty() {
  305. policyTagFilters := tagutils.STagFilters{}
  306. policyTagFilters.AddFilters(result.ObjectTags)
  307. q = ObjectIdQueryWithTagFilters(ctx, q, "id", manager.Keyword(), policyTagFilters)
  308. }
  309. }
  310. }
  311. } else {
  312. log.Debugf("res_scope: %s view_scope: %s", resScope, scope)
  313. // system view
  314. if userCred != nil {
  315. result := policy.PolicyManager.Allow(scope, userCred, consts.GetServiceType(), manager.KeywordPlural(), policy.PolicyActionList)
  316. log.Debugf("policy result: %s", jsonutils.Marshal(result))
  317. if !result.DomainTags.IsEmpty() && (resScope == rbacscope.ScopeDomain || resScope == rbacscope.ScopeProject) && scope == rbacscope.ScopeSystem {
  318. subq := manager.Query("id")
  319. policyTagFilters := tagutils.STagFilters{}
  320. policyTagFilters.AddFilters(result.DomainTags)
  321. subq = ObjectIdQueryWithTagFilters(ctx, subq, "domain_id", "domain", policyTagFilters)
  322. q = q.Filter(sqlchemy.OR(
  323. sqlchemy.In(q.Field("id"), subq.SubQuery()),
  324. sqlchemy.AND(
  325. sqlchemy.IsTrue(q.Field("is_public")),
  326. sqlchemy.Equals(q.Field("public_scope"), rbacscope.ScopeSystem),
  327. ),
  328. ))
  329. }
  330. if !result.ProjectTags.IsEmpty() && resScope == rbacscope.ScopeProject {
  331. subq := manager.Query("id")
  332. policyTagFilters := tagutils.STagFilters{}
  333. policyTagFilters.AddFilters(result.ProjectTags)
  334. subq = ObjectIdQueryWithTagFilters(ctx, subq, "tenant_id", "project", policyTagFilters)
  335. q = q.Filter(sqlchemy.OR(
  336. sqlchemy.In(q.Field("id"), subq.SubQuery()),
  337. sqlchemy.AND(
  338. sqlchemy.IsTrue(q.Field("is_public")),
  339. sqlchemy.Equals(q.Field("public_scope"), rbacscope.ScopeSystem),
  340. ),
  341. ))
  342. }
  343. if !result.ObjectTags.IsEmpty() {
  344. policyTagFilters := tagutils.STagFilters{}
  345. policyTagFilters.AddFilters(result.ObjectTags)
  346. q = ObjectIdQueryWithTagFilters(ctx, q, "id", manager.Keyword(), policyTagFilters)
  347. }
  348. }
  349. }
  350. }
  351. return q
  352. }
  353. type SSharableBaseResource struct {
  354. // 是否共享
  355. IsPublic bool `default:"false" nullable:"false" list:"user" create:"domain_optional"`
  356. // 默认共享范围
  357. PublicScope string `width:"16" charset:"ascii" nullable:"false" default:"system" list:"user" create:"domain_optional"`
  358. // 共享设置的来源, local: 本地设置, cloud: 从云上同步过来
  359. // example: local
  360. PublicSrc string `width:"10" charset:"ascii" nullable:"true" list:"user" json:"public_src"`
  361. }
  362. type ISharableBaseModel interface {
  363. IStandaloneModel
  364. ISharableBase
  365. }
  366. type ISharableBase interface {
  367. SetShare(scoe rbacscope.TRbacScope)
  368. GetIsPublic() bool
  369. GetPublicScope() rbacscope.TRbacScope
  370. GetSharableTargetDomainIds() []string
  371. GetRequiredSharedDomainIds() []string
  372. GetSharedDomains() []string
  373. }
  374. func ISharableChangeOwnerCandidateDomainIds(model ISharableBaseModel) []string {
  375. var candidates []string
  376. if model.GetIsPublic() {
  377. switch model.GetPublicScope() {
  378. case rbacscope.ScopeSystem:
  379. return candidates
  380. case rbacscope.ScopeDomain:
  381. candidates = model.GetSharedDomains()
  382. }
  383. }
  384. ownerId := model.GetOwnerId()
  385. if ownerId != nil && len(ownerId.GetProjectDomainId()) > 0 {
  386. candidates = append(candidates, ownerId.GetProjectDomainId())
  387. }
  388. return candidates
  389. }
  390. func ISharableMergeChangeOwnerCandidateDomainIds(model ISharableBaseModel, candidates ...[]string) []string {
  391. var ret stringutils2.SSortedStrings
  392. for i := range candidates {
  393. if len(candidates[i]) > 0 {
  394. cand := stringutils2.NewSortedStrings(candidates[i])
  395. ownerId := model.GetOwnerId()
  396. if ownerId != nil && len(ownerId.GetProjectDomainId()) > 0 && !cand.Contains(ownerId.GetProjectDomainId()) {
  397. cand = stringutils2.Append(cand, ownerId.GetProjectDomainId())
  398. }
  399. if len(ret) > 0 {
  400. ret = stringutils2.Intersect(ret, cand)
  401. } else {
  402. ret = stringutils2.NewSortedStrings(cand)
  403. }
  404. }
  405. }
  406. return ret
  407. }
  408. func ISharableMergeShareRequireDomainIds(requiredIds ...[]string) []string {
  409. var ret stringutils2.SSortedStrings
  410. for i := range requiredIds {
  411. if len(requiredIds[i]) > 0 {
  412. req := stringutils2.NewSortedStrings(requiredIds[i])
  413. if ret == nil {
  414. ret = req
  415. } else {
  416. ret = stringutils2.Merge(ret, req)
  417. }
  418. } else {
  419. return nil
  420. }
  421. }
  422. return ret
  423. }
  424. func SharableModelIsSharable(model ISharableBaseModel, reqUsrId mcclient.IIdentityProvider) bool {
  425. if model.GetIsPublic() && model.GetPublicScope() == rbacscope.ScopeSystem {
  426. return true
  427. }
  428. ownerId := model.GetOwnerId()
  429. if model.GetIsPublic() && model.GetPublicScope() == rbacscope.ScopeDomain {
  430. if ownerId != nil && ownerId.GetProjectDomainId() == reqUsrId.GetProjectDomainId() {
  431. return true
  432. }
  433. q := SharedResourceManager.Query().Equals("resource_id", model.GetId())
  434. q = q.Equals("resource_type", model.Keyword())
  435. q = q.Equals("target_project_id", reqUsrId.GetProjectDomainId())
  436. q = q.Equals("target_type", SharedTargetDomain)
  437. cnt, _ := q.CountWithError()
  438. if cnt > 0 {
  439. return true
  440. }
  441. }
  442. if model.GetPublicScope() == rbacscope.ScopeProject {
  443. if ownerId != nil && ownerId.GetProjectId() == reqUsrId.GetProjectId() {
  444. return true
  445. }
  446. q := SharedResourceManager.Query().Equals("resource_id", model.GetId())
  447. q = q.Equals("resource_type", model.Keyword())
  448. q = q.Equals("target_project_id", reqUsrId.GetProjectId())
  449. q = q.Equals("target_type", SharedTargetProject)
  450. cnt, _ := q.CountWithError()
  451. if cnt > 0 {
  452. return true
  453. }
  454. }
  455. return false
  456. }
  457. func (m *SSharableBaseResource) SetShare(scope rbacscope.TRbacScope) {
  458. pub := false
  459. if scope != rbacscope.ScopeNone {
  460. pub = true
  461. }
  462. m.IsPublic = pub
  463. m.PublicScope = string(scope)
  464. m.PublicSrc = string(apis.OWNER_SOURCE_LOCAL)
  465. }
  466. func (m SSharableBaseResource) GetIsPublic() bool {
  467. return m.IsPublic
  468. }
  469. func (m SSharableBaseResource) GetPublicScope() rbacscope.TRbacScope {
  470. return rbacscope.String2Scope(m.PublicScope)
  471. }
  472. func SharablePerformPublic(model ISharableBaseModel, ctx context.Context, userCred mcclient.TokenCredential, input apis.PerformPublicProjectInput) error {
  473. var err error
  474. resourceScope := model.GetModelManager().ResourceScope()
  475. targetScope := rbacscope.String2ScopeDefault(input.Scope, rbacscope.ScopeSystem)
  476. if resourceScope.HigherThan(targetScope) {
  477. return errors.Wrapf(httperrors.ErrNotSupported, "cannot share %s resource to %s", resourceScope, targetScope)
  478. }
  479. if len(input.SharedProjectIds) > 0 && len(input.SharedDomainIds) > 0 {
  480. return errors.Wrap(httperrors.ErrInputParameter, "cannot set shared_projects and shared_domains at the same time")
  481. } else if len(input.SharedProjectIds) > 0 && targetScope != rbacscope.ScopeProject {
  482. targetScope = rbacscope.ScopeProject
  483. } else if len(input.SharedDomainIds) > 0 && targetScope != rbacscope.ScopeDomain {
  484. targetScope = rbacscope.ScopeDomain
  485. }
  486. shareResult := apis.PerformPublicProjectInput{}
  487. shareResult.Scope = string(targetScope)
  488. candidateIds := model.GetSharableTargetDomainIds()
  489. requireIds := model.GetRequiredSharedDomainIds()
  490. switch targetScope {
  491. case rbacscope.ScopeProject:
  492. if len(requireIds) == 0 {
  493. return errors.Wrap(httperrors.ErrForbidden, "require to be shared to system")
  494. } else if len(requireIds) > 1 {
  495. return errors.Wrap(httperrors.ErrForbidden, "require to be shared to other domain")
  496. }
  497. // if len(input.SharedProjects) == 0 {
  498. // return errors.Wrap(httperrors.ErrEmptyRequest, "empty shared target project list")
  499. // }
  500. shareResult.SharedProjectIds, err = SharedResourceManager.shareToTarget(ctx, userCred, model, SharedTargetProject, input.SharedProjectIds, nil, nil)
  501. if err != nil {
  502. return errors.Wrap(err, "shareToTarget")
  503. }
  504. if len(shareResult.SharedProjectIds) == 0 {
  505. targetScope = rbacscope.ScopeNone
  506. }
  507. case rbacscope.ScopeDomain:
  508. if !consts.GetNonDefaultDomainProjects() {
  509. return errors.Wrap(httperrors.ErrForbidden, "not allow to share to domain when non_default_domain_projects turned off")
  510. }
  511. if len(requireIds) == 0 {
  512. return errors.Wrap(httperrors.ErrForbidden, "require to be shared to system")
  513. }
  514. _, err = SharedResourceManager.shareToTarget(ctx, userCred, model, SharedTargetProject, nil, nil, nil)
  515. if err != nil {
  516. return errors.Wrap(err, "shareToTarget clean projects")
  517. }
  518. shareResult.SharedDomainIds, err = SharedResourceManager.shareToTarget(ctx, userCred, model, SharedTargetDomain, input.SharedDomainIds, candidateIds, requireIds)
  519. if err != nil {
  520. return errors.Wrap(err, "shareToTarget add domains")
  521. }
  522. if len(shareResult.SharedDomainIds) == 0 && resourceScope == rbacscope.ScopeDomain {
  523. targetScope = rbacscope.ScopeNone
  524. }
  525. case rbacscope.ScopeSystem:
  526. if len(candidateIds) > 0 {
  527. return httperrors.NewForbiddenError("sharing is limited to domains %s", jsonutils.Marshal(candidateIds))
  528. }
  529. _, err = SharedResourceManager.shareToTarget(ctx, userCred, model, SharedTargetProject, nil, nil, nil)
  530. if err != nil {
  531. return errors.Wrap(err, "shareToTarget clean projects")
  532. }
  533. _, err = SharedResourceManager.shareToTarget(ctx, userCred, model, SharedTargetDomain, nil, nil, nil)
  534. if err != nil {
  535. return errors.Wrap(err, "shareToTarget clean domainss")
  536. }
  537. }
  538. allowScope, policyTags := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), model.KeywordPlural(), policy.PolicyActionPerform, "public")
  539. if targetScope.HigherThan(allowScope) {
  540. return errors.Wrapf(httperrors.ErrNotSufficientPrivilege, "require %s allow %s", targetScope, allowScope)
  541. }
  542. requireScope := model.GetPublicScope()
  543. if requireScope.HigherThan(allowScope) {
  544. return errors.Wrapf(httperrors.ErrNotSufficientPrivilege, "require %s allow %s", requireScope, allowScope)
  545. }
  546. err = objectConfirmPolicyTags(ctx, model, policyTags)
  547. if err != nil {
  548. return errors.Wrap(err, "objectConfirmPolicyTags")
  549. }
  550. _, err = Update(model, func() error {
  551. model.SetShare(targetScope)
  552. return nil
  553. })
  554. if err != nil {
  555. return errors.Wrap(err, "Update")
  556. }
  557. if targetScope != rbacscope.ScopeNone {
  558. OpsLog.LogEvent(model, ACT_PUBLIC, shareResult, userCred)
  559. logclient.AddActionLogWithContext(ctx, model, logclient.ACT_PUBLIC, shareResult, userCred, true)
  560. }
  561. model.GetIStandaloneModel().ClearSchedDescCache()
  562. return nil
  563. }
  564. func SharablePerformPrivate(model ISharableBaseModel, ctx context.Context, userCred mcclient.TokenCredential) error {
  565. if !model.GetIsPublic() && model.GetPublicScope() == rbacscope.ScopeNone {
  566. return nil
  567. }
  568. resourceScope := model.GetModelManager().ResourceScope()
  569. if resourceScope == rbacscope.ScopeDomain && !consts.GetNonDefaultDomainProjects() {
  570. return errors.Wrap(httperrors.ErrForbidden, "not allow to private domain resource")
  571. }
  572. requireIds := model.GetRequiredSharedDomainIds()
  573. if len(requireIds) == 0 {
  574. return errors.Wrap(httperrors.ErrForbidden, "require to be shared to system")
  575. } else if len(requireIds) > 1 {
  576. return errors.Wrap(httperrors.ErrForbidden, "require to be shared to other domain")
  577. }
  578. requireScope := model.GetPublicScope()
  579. allowScope, policyTags := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), model.GetModelManager().KeywordPlural(), policy.PolicyActionPerform, "private")
  580. if requireScope.HigherThan(allowScope) {
  581. return errors.Wrapf(httperrors.ErrNotSufficientPrivilege, "require %s allow %s", requireScope, allowScope)
  582. }
  583. err := objectConfirmPolicyTags(ctx, model, policyTags)
  584. if err != nil {
  585. return errors.Wrap(err, "objectConfirmPolicyTags")
  586. }
  587. err = SharedResourceManager.CleanModelShares(ctx, userCred, model)
  588. if err != nil {
  589. return errors.Wrap(err, "CleanModelShares")
  590. }
  591. diff, err := Update(model, func() error {
  592. model.SetShare(rbacscope.ScopeNone)
  593. return nil
  594. })
  595. if err != nil {
  596. return errors.Wrap(err, "Update")
  597. }
  598. OpsLog.LogEvent(model, ACT_PRIVATE, diff, userCred)
  599. logclient.AddActionLogWithContext(ctx, model, logclient.ACT_PRIVATE, diff, userCred, true)
  600. model.GetIStandaloneModel().ClearSchedDescCache()
  601. return nil
  602. }
  603. func SharableGetSharedProjects(model ISharableBaseModel, targetType string) []string {
  604. sharedResources := make([]SSharedResource, 0)
  605. q := SharedResourceManager.Query()
  606. q = q.Equals("resource_type", model.Keyword())
  607. q = q.Equals("resource_id", model.GetId())
  608. q = q.Equals("target_type", targetType)
  609. err := q.All(&sharedResources)
  610. if err != nil {
  611. return nil
  612. }
  613. res := make([]string, len(sharedResources))
  614. for i := range sharedResources {
  615. res[i] = sharedResources[i].TargetProjectId
  616. }
  617. return res
  618. }
  619. func SharableModelIsShared(model ISharableBaseModel) bool {
  620. q := SharedResourceManager.Query()
  621. q = q.Equals("resource_type", model.Keyword())
  622. q = q.Equals("resource_id", model.GetId())
  623. cnt, _ := q.CountWithError()
  624. if cnt > 0 {
  625. return true
  626. }
  627. switch model.GetPublicScope() {
  628. case rbacscope.ScopeSystem:
  629. if model.GetIsPublic() {
  630. return true
  631. }
  632. case rbacscope.ScopeDomain:
  633. if model.GetModelManager().ResourceScope() == rbacscope.ScopeProject {
  634. return true
  635. }
  636. }
  637. return false
  638. }
  639. func SharableModelCustomizeCreate(model ISharableBaseModel, ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
  640. if !data.Contains("public_scope") {
  641. resScope := model.GetModelManager().ResourceScope()
  642. if resScope == rbacscope.ScopeDomain && consts.GetNonDefaultDomainProjects() {
  643. // only if non_default_domain_projects turned on, do the following
  644. isManaged := false
  645. if managedModel, ok := model.(IManagedResourceBase); ok {
  646. isManaged = managedModel.IsManaged()
  647. }
  648. // log.Debugf("isManaged: %v IsAdminAllowPerform %v ownerId.GetProjectDomainId %s userCred.GetProjectDomainId %s", isManaged, IsAdminAllowPerform(ctx, userCred, model, "public"), ownerId.GetProjectDomainId(), userCred.GetProjectDomainId())
  649. if !isManaged && IsAdminAllowPerform(ctx, userCred, model, "public") && ownerId.GetProjectDomainId() == userCred.GetProjectDomainId() {
  650. model.SetShare(rbacscope.ScopeSystem)
  651. data.(*jsonutils.JSONDict).Set("public_scope", jsonutils.NewString(string(rbacscope.ScopeSystem)))
  652. }
  653. }
  654. }
  655. if !data.Contains("public_scope") {
  656. model.SetShare(rbacscope.ScopeNone)
  657. }
  658. return nil
  659. }