clouduser.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  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 huawei
  15. import (
  16. "fmt"
  17. "net/url"
  18. "strings"
  19. "time"
  20. "yunion.io/x/pkg/errors"
  21. api "yunion.io/x/cloudmux/pkg/apis/cloudid"
  22. "yunion.io/x/cloudmux/pkg/cloudprovider"
  23. "yunion.io/x/cloudmux/pkg/multicloud"
  24. )
  25. type SLink struct {
  26. Next string
  27. Previous string
  28. Self string
  29. }
  30. type SClouduser struct {
  31. client *SHuaweiClient
  32. multicloud.SBaseClouduser
  33. Description string
  34. DomainId string
  35. Enabled bool
  36. ForceResetPwd bool
  37. Id string
  38. LastProjectId string
  39. Links SLink
  40. Name string
  41. PasswordExpiresAt string
  42. PwdStatus bool
  43. AccessMode string
  44. }
  45. func (user *SClouduser) GetGlobalId() string {
  46. return user.Id
  47. }
  48. func (user *SClouduser) GetName() string {
  49. return user.Name
  50. }
  51. func (user *SClouduser) GetEmailAddr() string {
  52. return ""
  53. }
  54. func (user *SClouduser) GetInviteUrl() string {
  55. return ""
  56. }
  57. func (user *SClouduser) GetICloudpolicies() ([]cloudprovider.ICloudpolicy, error) {
  58. return []cloudprovider.ICloudpolicy{}, nil
  59. }
  60. func (user *SClouduser) AttachPolicy(policyName string, policyType api.TPolicyType) error {
  61. return cloudprovider.ErrNotSupported
  62. }
  63. func (user *SClouduser) DetachPolicy(policyName string, policyType api.TPolicyType) error {
  64. return cloudprovider.ErrNotSupported
  65. }
  66. func (user *SClouduser) GetICloudgroups() ([]cloudprovider.ICloudgroup, error) {
  67. groups, err := user.client.ListUserGroups(user.Id)
  68. if err != nil {
  69. return nil, errors.Wrap(err, "Users.ListGroups")
  70. }
  71. ret := []cloudprovider.ICloudgroup{}
  72. for i := range groups {
  73. groups[i].client = user.client
  74. ret = append(ret, &groups[i])
  75. }
  76. return ret, nil
  77. }
  78. func (user *SClouduser) Delete() error {
  79. return user.client.DeleteClouduser(user.Id)
  80. }
  81. func (user *SClouduser) IsConsoleLogin() bool {
  82. return user.AccessMode != "programmatic"
  83. }
  84. func (user *SClouduser) SetDisable() error {
  85. enable := false
  86. return user.client.UpdateUser(user.Id, "", &enable)
  87. }
  88. func (user *SClouduser) SetEnable(opts *cloudprovider.SClouduserEnableOptions) error {
  89. enable := true
  90. err := user.client.UpdateUser(user.Id, opts.Password, &enable)
  91. if err != nil {
  92. return err
  93. }
  94. if opts.EnableMfa {
  95. return user.client.EnableUserMfa(user.Id)
  96. }
  97. return nil
  98. }
  99. func (self *SHuaweiClient) EnableUserMfa(id string) error {
  100. params := map[string]interface{}{
  101. "login_protect": map[string]interface{}{
  102. "enabled": "true",
  103. "verification_method": "vmfa",
  104. },
  105. }
  106. _, err := self.put(SERVICE_IAM, "", fmt.Sprintf("OS-USER/users/%s/login-protect", id), params)
  107. return err
  108. }
  109. func (user *SClouduser) ResetPassword(password string) error {
  110. return user.client.UpdateUser(user.Id, password, nil)
  111. }
  112. // https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=KeystoneDeleteUser
  113. func (self *SHuaweiClient) DeleteClouduser(id string) error {
  114. _, err := self.delete(SERVICE_IAM_V3, "", "users/"+id)
  115. return err
  116. }
  117. // https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=KeystoneListGroupsForUser
  118. func (self *SHuaweiClient) ListUserGroups(userId string) ([]SCloudgroup, error) {
  119. resp, err := self.list(SERVICE_IAM_V3, "", fmt.Sprintf("users/%s/groups", userId), nil)
  120. if err != nil {
  121. return nil, err
  122. }
  123. groups := []SCloudgroup{}
  124. err = resp.Unmarshal(&groups, "groups")
  125. if err != nil {
  126. return nil, err
  127. }
  128. return groups, nil
  129. }
  130. // https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=KeystoneListUsers
  131. func (self *SHuaweiClient) GetCloudusers(name string) ([]SClouduser, error) {
  132. params := url.Values{}
  133. if len(name) > 0 {
  134. params.Set("name", name)
  135. }
  136. resp, err := self.list(SERVICE_IAM_V3, "", "users", params)
  137. if err != nil {
  138. return nil, err
  139. }
  140. users := []SClouduser{}
  141. err = resp.Unmarshal(&users, "users")
  142. if err != nil {
  143. return nil, err
  144. }
  145. return users, nil
  146. }
  147. func (self *SHuaweiClient) GetICloudusers() ([]cloudprovider.IClouduser, error) {
  148. users, err := self.GetCloudusers("")
  149. if err != nil {
  150. return nil, errors.Wrap(err, "GetCloudusers")
  151. }
  152. iUsers := []cloudprovider.IClouduser{}
  153. for i := range users {
  154. if users[i].Name != self.ownerName {
  155. users[i].client = self
  156. iUsers = append(iUsers, &users[i])
  157. }
  158. }
  159. return iUsers, nil
  160. }
  161. func (self *SHuaweiClient) GetIClouduserByName(name string) (cloudprovider.IClouduser, error) {
  162. users, err := self.GetCloudusers(name)
  163. if err != nil {
  164. return nil, errors.Wrapf(err, "GetCloudusers(%s)", name)
  165. }
  166. if len(users) == 0 {
  167. return nil, cloudprovider.ErrNotFound
  168. }
  169. if len(users) > 1 {
  170. return nil, cloudprovider.ErrDuplicateId
  171. }
  172. users[0].client = self
  173. return &users[0], nil
  174. }
  175. func (self *SHuaweiClient) CreateIClouduser(conf *cloudprovider.SClouduserCreateConfig) (cloudprovider.IClouduser, error) {
  176. return self.CreateClouduser(conf.Name, conf.Password, conf.Desc)
  177. }
  178. // https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=CreateUser
  179. func (self *SHuaweiClient) CreateClouduser(name, password, desc string) (*SClouduser, error) {
  180. params := map[string]interface{}{
  181. "name": name,
  182. "domain_id": self.ownerId,
  183. }
  184. if len(password) > 0 {
  185. params["password"] = password
  186. }
  187. if len(desc) > 0 {
  188. params["description"] = desc
  189. }
  190. resp, err := self.post(SERVICE_IAM, "", "OS-USER/users", map[string]interface{}{"user": params})
  191. if err != nil {
  192. if strings.Contains(err.Error(), "1101") {
  193. return nil, errors.Wrap(err, `IAM user name. The length is between 5 and 32. The first digit is not a number. Special characters can only contain the '_' '-' or ' '`) //https://support.huaweicloud.com/api-iam/iam_08_0015.html
  194. }
  195. return nil, err
  196. }
  197. user := &SClouduser{client: self}
  198. err = resp.Unmarshal(user, "user")
  199. if err != nil {
  200. return nil, err
  201. }
  202. return user, nil
  203. }
  204. // https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=UpdateUser
  205. func (self *SHuaweiClient) UpdateUser(id, password string, enable *bool) error {
  206. params := map[string]interface{}{
  207. "enabled": true,
  208. }
  209. if len(password) > 0 {
  210. params["password"] = password
  211. }
  212. if enable != nil {
  213. params["access_mode"] = "programmatic"
  214. if *enable {
  215. params["access_mode"] = "default"
  216. }
  217. }
  218. _, err := self.put(SERVICE_IAM, "", "OS-USER/users/"+id, map[string]interface{}{
  219. "user": params,
  220. })
  221. return err
  222. }
  223. type SAccessKey struct {
  224. client *SHuaweiClient
  225. AccessKey string `json:"access"`
  226. Secret string `json:"secret"`
  227. Description string `json:"description"`
  228. Status string `json:"status"`
  229. CreatedAt time.Time `json:"create_time"`
  230. }
  231. // https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=ListPermanentAccessKeys
  232. func (self *SHuaweiClient) GetAKSK(id string) ([]cloudprovider.SAccessKey, error) {
  233. query := url.Values{}
  234. query.Set("user_id", id)
  235. obj, err := self.list(SERVICE_IAM, "", "OS-CREDENTIAL/credentials", query)
  236. if err != nil {
  237. return nil, errors.Wrap(err, "list credential")
  238. }
  239. aks := make([]SAccessKey, 0)
  240. obj.Unmarshal(&aks, "credentials")
  241. res := make([]cloudprovider.SAccessKey, len(aks))
  242. for i := 0; i < len(aks); i++ {
  243. res[i].Name = aks[i].Description
  244. res[i].AccessKey = aks[i].AccessKey
  245. res[i].Secret = aks[i].Secret
  246. res[i].Status = aks[i].Status
  247. res[i].CreatedAt = aks[i].CreatedAt
  248. }
  249. return res, nil
  250. }
  251. // https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=CreatePermanentAccessKey
  252. func (self *SHuaweiClient) CreateAKSK(id, name string) (*cloudprovider.SAccessKey, error) {
  253. params := map[string]interface{}{
  254. "credential": map[string]interface{}{
  255. "user_id": id,
  256. "description": name,
  257. },
  258. }
  259. obj, err := self.post(SERVICE_IAM, "", "OS-CREDENTIAL/credentials", params)
  260. if err != nil {
  261. return nil, errors.Wrap(err, "SHuaweiClient.createAKSK")
  262. }
  263. ak := SAccessKey{}
  264. err = obj.Unmarshal(&ak, "credential")
  265. if err != nil {
  266. return nil, errors.Wrapf(err, "Unmarshal")
  267. }
  268. res := cloudprovider.SAccessKey{
  269. Name: ak.Description,
  270. AccessKey: ak.AccessKey,
  271. Secret: ak.Secret,
  272. }
  273. return &res, nil
  274. }
  275. // https://console.huaweicloud.com/apiexplorer/#/openapi/IAM/doc?api=DeletePermanentAccessKey
  276. func (self *SHuaweiClient) DeleteAKSK(accessKey string) error {
  277. _, err := self.delete(SERVICE_IAM, "", "OS-CREDENTIAL/credentials/"+accessKey)
  278. return err
  279. }
  280. func (user *SClouduser) DeleteAccessKey(accessKey string) error {
  281. return user.client.DeleteAKSK(accessKey)
  282. }
  283. func (user *SClouduser) CreateAccessKey(name string) (*cloudprovider.SAccessKey, error) {
  284. return user.client.CreateAKSK(user.Id, name)
  285. }
  286. func (user *SClouduser) GetAccessKeys() ([]cloudprovider.SAccessKey, error) {
  287. return user.client.GetAKSK(user.Id)
  288. }