iampolicy.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  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. // Copyright 2019 Yunion
  15. //
  16. // Licensed under the Apache License, Version 2.0 (the "License");
  17. // you may not use this file except in compliance with the License.
  18. // You may obtain a copy of the License at
  19. //
  20. // http://www.apache.org/licenses/LICENSE-2.0
  21. //
  22. // Unless required by applicable law or agreed to in writing, software
  23. // distributed under the License is distributed on an "AS IS" BASIS,
  24. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  25. // See the License for the specific language governing permissions and
  26. // limitations under the License.
  27. package google
  28. import (
  29. "fmt"
  30. "strings"
  31. "yunion.io/x/jsonutils"
  32. "yunion.io/x/pkg/errors"
  33. "yunion.io/x/pkg/util/stringutils"
  34. "yunion.io/x/pkg/utils"
  35. api "yunion.io/x/cloudmux/pkg/apis/cloudid"
  36. "yunion.io/x/cloudmux/pkg/cloudprovider"
  37. "yunion.io/x/cloudmux/pkg/multicloud"
  38. )
  39. type SBinding struct {
  40. Role string
  41. Members []string
  42. }
  43. type SIamPolicy struct {
  44. client *SGoogleClient
  45. Version int
  46. Etag string
  47. Bindings []SBinding
  48. }
  49. func (self *SGoogleClient) GetIamPolicy() (*SIamPolicy, error) {
  50. resource := fmt.Sprintf("projects/%s:getIamPolicy", self.projectId)
  51. resp, err := self.managerPost(resource, nil, nil)
  52. if err != nil {
  53. return nil, errors.Wrap(err, "managerPost")
  54. }
  55. policy := &SIamPolicy{client: self}
  56. err = resp.Unmarshal(policy)
  57. if err != nil {
  58. return nil, errors.Wrap(err, "Unmarshal")
  59. }
  60. return policy, nil
  61. }
  62. func (self *SGoogleClient) TestIam(permissions []string) ([]string, error) {
  63. resource := fmt.Sprintf("projects/%s:testIamPermissions", self.projectId)
  64. body := jsonutils.Marshal(map[string]interface{}{"permissions": permissions})
  65. resp, err := self.managerPost(resource, nil, body)
  66. if err != nil {
  67. return nil, errors.Wrap(err, "testIamPermissions")
  68. }
  69. ret := struct {
  70. Permissions []string
  71. }{}
  72. err = resp.Unmarshal(&ret)
  73. if err != nil {
  74. return nil, errors.Wrap(err, "resp.Unmarshal")
  75. }
  76. return ret.Permissions, nil
  77. }
  78. func (self *SGoogleClient) IsSupportCloudId() bool {
  79. permissions, err := self.TestIam([]string{"resourcemanager.projects.setIamPolicy"})
  80. if err != nil {
  81. return false
  82. }
  83. return len(permissions) == 1
  84. }
  85. func (self *SGoogleClient) SetIamPlicy(policy *SIamPolicy) error {
  86. resource := fmt.Sprintf("projects/%s:setIamPolicy", self.projectId)
  87. body := jsonutils.Marshal(map[string]interface{}{"policy": map[string]interface{}{"bindings": policy.Bindings}})
  88. _, err := self.managerPost(resource, nil, body)
  89. if err != nil {
  90. return errors.Wrap(err, "managerPost")
  91. }
  92. return nil
  93. }
  94. func (self *SGoogleClient) CreateICloudpolicy(opts *cloudprovider.SCloudpolicyCreateOptions) (cloudprovider.ICloudpolicy, error) {
  95. permission := struct {
  96. IncludedPermissions []string
  97. }{}
  98. err := opts.Document.Unmarshal(&permission)
  99. if err != nil {
  100. return nil, errors.Wrapf(err, "Document.Unmarshal(")
  101. }
  102. return self.CreateRole(permission.IncludedPermissions, opts.Name, opts.Desc)
  103. }
  104. func (self *SGoogleClient) CreateRole(permissions []string, name, desc string) (*SRole, error) {
  105. resource := fmt.Sprintf("projects/%s/roles", self.projectId)
  106. params := map[string]interface{}{
  107. "roleId": strings.ReplaceAll(stringutils.UUID4(), "-", "_"),
  108. "role": map[string]interface{}{
  109. "title": name,
  110. "description": desc,
  111. "includedPermissions": permissions,
  112. "stage": "GA",
  113. },
  114. }
  115. resp, err := self.iamPost(resource, nil, jsonutils.Marshal(params))
  116. if err != nil {
  117. return nil, errors.Wrap(err, "managerPost")
  118. }
  119. role := SRole{client: self}
  120. err = resp.Unmarshal(&role)
  121. if err != nil {
  122. return nil, errors.Wrap(err, "resp.Unmarshal")
  123. }
  124. return &role, nil
  125. }
  126. type SClouduser struct {
  127. multicloud.SBaseClouduser
  128. policy *SIamPolicy
  129. Name string
  130. Roles []string
  131. }
  132. func (self *SClouduser) GetEmailAddr() string {
  133. return ""
  134. }
  135. func (self *SClouduser) GetInviteUrl() string {
  136. return ""
  137. }
  138. func (self *SGoogleClient) GetICloudpolicies() ([]cloudprovider.ICloudpolicy, error) {
  139. roles, err := self.GetRoles("")
  140. if err != nil {
  141. return nil, errors.Wrap(err, "GetRoles")
  142. }
  143. ret := []cloudprovider.ICloudpolicy{}
  144. for i := range roles {
  145. roles[i].client = self
  146. ret = append(ret, &roles[i])
  147. }
  148. return ret, nil
  149. }
  150. type SRole struct {
  151. client *SGoogleClient
  152. Name string
  153. Title string
  154. Description string
  155. IncludedPermissions []string
  156. Stage string
  157. Etag string
  158. }
  159. func (role *SRole) GetName() string {
  160. return role.Title
  161. }
  162. func (role *SRole) GetGlobalId() string {
  163. return role.Name
  164. }
  165. func (role *SRole) GetDescription() string {
  166. return role.Description
  167. }
  168. func (role *SRole) GetPolicyType() api.TPolicyType {
  169. return api.PolicyTypeSystem
  170. }
  171. func (role *SRole) UpdateDocument(document *jsonutils.JSONDict) error {
  172. permissions := struct {
  173. IncludedPermissions []string
  174. }{}
  175. err := document.Unmarshal(&permissions)
  176. if err != nil {
  177. return errors.Wrapf(err, "document.Unmarshal")
  178. }
  179. return role.client.UpdateRole(role.Name, permissions.IncludedPermissions)
  180. }
  181. func (role *SRole) Delete() error {
  182. return role.client.DeleteRole(role.Name)
  183. }
  184. func (role *SRole) GetDocument() (*jsonutils.JSONDict, error) {
  185. permissions := jsonutils.Marshal(role.IncludedPermissions)
  186. result := jsonutils.NewDict()
  187. result.Add(permissions, "includedPermissions")
  188. return result, nil
  189. }
  190. func (self *SGoogleClient) GetRole(roleId string) (*SRole, error) {
  191. role := &SRole{}
  192. resp, err := self.iamGet(roleId)
  193. if err != nil {
  194. return nil, errors.Wrapf(err, "iamGet(%s)", roleId)
  195. }
  196. err = resp.Unmarshal(role)
  197. if err != nil {
  198. return nil, errors.Wrap(err, "resp.Unmarshal")
  199. }
  200. return role, nil
  201. }
  202. // https://cloud.google.com/iam/docs/reference/rest/v1/roles/list
  203. func (self *SGoogleClient) GetRoles(projectId string) ([]SRole, error) {
  204. roles := []SRole{}
  205. params := map[string]string{"view": "FULL"}
  206. resource := "roles"
  207. if len(projectId) > 0 {
  208. resource = fmt.Sprintf("projects/%s/roles", projectId)
  209. }
  210. err := self.iamListAll(resource, params, &roles)
  211. if err != nil {
  212. return nil, errors.Wrap(err, "iamListAll.roles")
  213. }
  214. return roles, nil
  215. }
  216. func (user *SClouduser) GetGlobalId() string {
  217. return user.Name
  218. }
  219. func (user *SClouduser) GetName() string {
  220. return user.Name
  221. }
  222. func (user *SClouduser) IsConsoleLogin() bool {
  223. return true
  224. }
  225. func (user *SClouduser) GetICloudpolicies() ([]cloudprovider.ICloudpolicy, error) {
  226. ret := []cloudprovider.ICloudpolicy{}
  227. for _, roleStr := range user.Roles {
  228. if strings.HasPrefix(roleStr, "roles/") {
  229. role, err := user.policy.client.GetRole(roleStr)
  230. if err != nil {
  231. return nil, errors.Wrap(err, "GetRole")
  232. }
  233. ret = append(ret, role)
  234. }
  235. }
  236. return ret, nil
  237. }
  238. var getUserName = func(user string) string {
  239. if strings.HasSuffix(user, "gserviceaccount.com") {
  240. return "serviceAccount:" + user
  241. }
  242. return "user:" + user
  243. }
  244. func (policy *SIamPolicy) AttachPolicy(user string, roles []string) error {
  245. for _, role := range roles {
  246. find := false
  247. for i := range policy.Bindings {
  248. if policy.Bindings[i].Role == role {
  249. if !utils.IsInStringArray(getUserName(user), policy.Bindings[i].Members) {
  250. policy.Bindings[i].Members = append(policy.Bindings[i].Members, getUserName(user))
  251. find = true
  252. }
  253. }
  254. }
  255. if !find {
  256. policy.Bindings = append(policy.Bindings, SBinding{Role: role, Members: []string{getUserName(user)}})
  257. }
  258. }
  259. return policy.client.SetIamPlicy(policy)
  260. }
  261. func (user *SClouduser) AttachPolicy(role string, policyType api.TPolicyType) error {
  262. return user.policy.AttachPolicy(user.Name, []string{role})
  263. }
  264. func (user *SClouduser) AttachCustomPolicy(role string) error {
  265. return user.policy.AttachPolicy(user.Name, []string{role})
  266. }
  267. func (policy *SIamPolicy) DetachPolicy(user, role string) error {
  268. change := false
  269. for i := range policy.Bindings {
  270. if policy.Bindings[i].Role == role && utils.IsInStringArray(getUserName(user), policy.Bindings[i].Members) {
  271. change = true
  272. members := []string{}
  273. for _, member := range policy.Bindings[i].Members {
  274. if member != getUserName(user) {
  275. members = append(members, member)
  276. }
  277. }
  278. policy.Bindings[i].Members = members
  279. }
  280. }
  281. if change {
  282. return policy.client.SetIamPlicy(policy)
  283. }
  284. return nil
  285. }
  286. func (client *SGoogleClient) CreateIClouduser(conf *cloudprovider.SClouduserCreateConfig) (cloudprovider.IClouduser, error) {
  287. return nil, cloudprovider.ErrNotImplemented
  288. }
  289. func (client *SGoogleClient) GetIClouduserByName(name string) (cloudprovider.IClouduser, error) {
  290. policy, err := client.GetIamPolicy()
  291. if err != nil {
  292. return nil, errors.Wrap(err, "GetIamPolicy")
  293. }
  294. users, err := policy.GetICloudusers()
  295. if err != nil {
  296. return nil, errors.Wrap(err, "policy.GetICloudusers")
  297. }
  298. for i := range users {
  299. if users[i].GetName() == name {
  300. return users[i], nil
  301. }
  302. }
  303. return &SClouduser{policy: policy, Name: name, Roles: []string{}}, nil
  304. }
  305. func (user *SClouduser) DetachPolicy(role string, policyType api.TPolicyType) error {
  306. return user.policy.DetachPolicy(user.Name, role)
  307. }
  308. func (policy *SIamPolicy) DeleteUser(user string) error {
  309. change := false
  310. for i := range policy.Bindings {
  311. if utils.IsInStringArray(getUserName(user), policy.Bindings[i].Members) {
  312. change = true
  313. members := []string{}
  314. for _, member := range policy.Bindings[i].Members {
  315. if member != getUserName(user) {
  316. members = append(members, member)
  317. }
  318. }
  319. policy.Bindings[i].Members = members
  320. }
  321. }
  322. if change {
  323. return policy.client.SetIamPlicy(policy)
  324. }
  325. return nil
  326. }
  327. func (user *SClouduser) Delete() error {
  328. return user.policy.DeleteUser(user.Name)
  329. }
  330. func (user *SClouduser) GetICloudgroups() ([]cloudprovider.ICloudgroup, error) {
  331. return []cloudprovider.ICloudgroup{}, nil
  332. }
  333. func (user *SClouduser) ResetPassword(password string) error {
  334. return cloudprovider.ErrNotSupported
  335. }
  336. func (client *SGoogleClient) GetICloudusers() ([]cloudprovider.IClouduser, error) {
  337. policy, err := client.GetIamPolicy()
  338. if err != nil {
  339. return nil, errors.Wrap(err, "GetIamPolicy")
  340. }
  341. return policy.GetICloudusers()
  342. }
  343. func (policy *SIamPolicy) GetICloudusers() ([]cloudprovider.IClouduser, error) {
  344. users := map[string]*SClouduser{}
  345. for _, binding := range policy.Bindings {
  346. for _, member := range binding.Members {
  347. if strings.HasPrefix(member, "user:") {
  348. user := strings.TrimPrefix(member, "user:")
  349. if _, ok := users[user]; !ok {
  350. users[user] = &SClouduser{Name: user, Roles: []string{}}
  351. }
  352. roles := users[user].Roles
  353. if !utils.IsInStringArray(binding.Role, roles) {
  354. roles = append(roles, binding.Role)
  355. }
  356. users[user].Roles = roles
  357. }
  358. }
  359. }
  360. cloudusers := []cloudprovider.IClouduser{}
  361. for i := range users {
  362. users[i].policy = policy
  363. cloudusers = append(cloudusers, users[i])
  364. }
  365. return cloudusers, nil
  366. }
  367. func (self *SGoogleClient) DeleteRole(id string) error {
  368. return self.iamDelete(id, nil)
  369. }
  370. func (self *SGoogleClient) UpdateRole(id string, permissions []string) error {
  371. query := map[string]string{
  372. "updateMask": "includedPermissions",
  373. }
  374. params := map[string]interface{}{
  375. "includedPermissions": permissions,
  376. }
  377. _, err := self.iamPatch(id, query, jsonutils.Marshal(params))
  378. return err
  379. }