From 4f43e6d121d0ad7a1dde50b3f091fdbe3b93ae56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=88=E8=BD=A9?= Date: Tue, 2 Apr 2024 14:39:18 +0800 Subject: [PATCH] fix(cloudid): vendor update form volcengine iam (#19856) --- go.mod | 2 +- go.sum | 4 +- pkg/cloudid/drivers/base.go | 48 ++- pkg/cloudid/drivers/volcengine.go | 32 ++ pkg/cloudid/models/cloudaccount.go | 26 -- pkg/cloudid/models/samldriver.go | 1 + pkg/cloudid/saml/load.go | 1 + pkg/cloudid/saml/providers/volcengine/doc.go | 1 + .../saml/providers/volcengine/driver.go | 95 +++++ .../saml/providers/volcengine/factory.go | 39 +++ pkg/cloudid/service/service.go | 4 +- .../tasks/cloudgroup_set_policies_task.go | 4 +- pkg/cloudid/tasks/clouduser_create_task.go | 2 +- vendor/modules.txt | 2 +- .../x/cloudmux/pkg/cloudprovider/resources.go | 4 +- .../x/cloudmux/pkg/cloudprovider/saml.go | 2 + .../pkg/multicloud/aliyun/ram_role.go | 9 +- .../x/cloudmux/pkg/multicloud/aws/iam_role.go | 4 +- .../pkg/multicloud/qcloud/cam_role.go | 4 +- .../pkg/multicloud/volcengine/group.go | 298 ++++++++++++++++ .../pkg/multicloud/volcengine/policy.go | 143 ++++++++ .../pkg/multicloud/volcengine/project.go | 10 +- .../volcengine/provider/provider.go | 78 +++++ .../pkg/multicloud/volcengine/role.go | 224 ++++++++++++ .../multicloud/volcengine/saml_provider.go | 202 +++++++++++ .../pkg/multicloud/volcengine/user.go | 328 ++++++++++++++++-- .../pkg/multicloud/volcengine/volcengine.go | 131 +++++-- 27 files changed, 1586 insertions(+), 112 deletions(-) create mode 100644 pkg/cloudid/drivers/volcengine.go create mode 100644 pkg/cloudid/saml/providers/volcengine/doc.go create mode 100644 pkg/cloudid/saml/providers/volcengine/driver.go create mode 100644 pkg/cloudid/saml/providers/volcengine/factory.go create mode 100644 vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/group.go create mode 100644 vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/policy.go create mode 100644 vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/role.go create mode 100644 vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/saml_provider.go diff --git a/go.mod b/go.mod index e6b8b2e923..dc96dd003b 100644 --- a/go.mod +++ b/go.mod @@ -88,7 +88,7 @@ require ( k8s.io/client-go v0.19.3 k8s.io/cluster-bootstrap v0.19.3 moul.io/http2curl/v2 v2.3.0 - yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240401032225-9a152ed52202 + yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240402061507-4922cc681244 yunion.io/x/executor v0.0.0-20230705125604-c5ac3141db32 yunion.io/x/jsonutils v1.0.1-0.20240203102553-4096f103b401 yunion.io/x/log v1.0.1-0.20240305175729-7cf2d6cd5a91 diff --git a/go.sum b/go.sum index a028b5dd64..60e7b733d8 100644 --- a/go.sum +++ b/go.sum @@ -1204,8 +1204,8 @@ sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= -yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240401032225-9a152ed52202 h1:nufuDeVPNxkYT8IbVbkwSb4SbGYxWV1w1DhrhR7++tE= -yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240401032225-9a152ed52202/go.mod h1:dsUESXIbXJ+/ywbNClhldOrbPOiBi2udrgOnB/ffoWk= +yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240402061507-4922cc681244 h1:b63UITGOFheDihKXXzF1fPX/Cgb/zDa3a93cs9pmmkA= +yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240402061507-4922cc681244/go.mod h1:dsUESXIbXJ+/ywbNClhldOrbPOiBi2udrgOnB/ffoWk= yunion.io/x/executor v0.0.0-20230705125604-c5ac3141db32 h1:v7POYkQwo1XzOxBoIoRVr/k0V9Y5JyjpshlIFa9raug= yunion.io/x/executor v0.0.0-20230705125604-c5ac3141db32/go.mod h1:Uxuou9WQIeJXNpy7t2fPLL0BYLvLiMvGQwY7Qc6aSws= yunion.io/x/jsonutils v0.0.0-20190625054549-a964e1e8a051/go.mod h1:4N0/RVzsYL3kH3WE/H1BjUQdFiWu50JGCFQuuy+Z634= diff --git a/pkg/cloudid/drivers/base.go b/pkg/cloudid/drivers/base.go index 5dbb01321e..c27061973f 100644 --- a/pkg/cloudid/drivers/base.go +++ b/pkg/cloudid/drivers/base.go @@ -22,6 +22,7 @@ import ( "yunion.io/x/cloudmux/pkg/cloudprovider" "yunion.io/x/log" "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/utils" "yunion.io/x/onecloud/pkg/apis" api "yunion.io/x/onecloud/pkg/apis/cloudid" @@ -436,7 +437,7 @@ func (base SProviderBaseProviderDriver) RequestCreateClouduser(ctx context.Conte iUser, err := provider.CreateIClouduser(opts) if err != nil { - return errors.Wrapf(err, "CreateICloudgroup") + return errors.Wrapf(err, "CreateIClouduser") } _, err = db.Update(user, func() error { user.ExternalId = iUser.GetGlobalId() @@ -537,8 +538,11 @@ func (base SProviderBaseProviderDriver) RequestCreateSAMLProvider(ctx context.Co opts := &cloudprovider.SAMLProviderCreateOptions{ Metadata: models.SamlIdpInstance().GetMetadata(providers[i].Id), Name: strings.TrimPrefix(options.Options.ApiServer, "https://"), + Desc: "create by cloudpods", } + log.Debugf("create saml provider for manager %s(%s) %s", providers[i].Name, providers[i].Id, opts.Metadata.String()) + opts.Name = strings.TrimPrefix(opts.Name, "http://") iSaml, err := iProvider.CreateICloudSAMLProvider(opts) @@ -577,17 +581,44 @@ func (base SProviderBaseProviderDriver) RequestCreateRoleForSamlUser(ctx context if err != nil { return errors.Wrapf(err, "GetSamlProvider") } + policies, err := group.GetCloudpolicies() + if err != nil { + return errors.Wrapf(err, "GetCloudpolicies") + } roles, err := account.GetCloudroles(provider.Id) if err != nil { return errors.Wrapf(err, "GetCloudroles") } for i := range roles { - if roles[i].Status == apis.STATUS_AVAILABLE && len(roles[i].ExternalId) > 0 && roles[i].SAMLProviderId == samlProvider.Id { + if roles[i].Status == apis.STATUS_AVAILABLE && len(roles[i].ExternalId) > 0 && roles[i].SAMLProviderId == samlProvider.Id && roles[i].CloudgroupId == group.Id { _, err := db.Update(user, func() error { user.CloudroleId = roles[i].Id return nil }) - return err + if err != nil { + return err + } + existPolicies := []string{} + iRole, err := roles[i].GetICloudrole() + if err != nil { + return err + } + iPolicies, err := iRole.GetICloudpolicies() + if err != nil { + return errors.Wrapf(err, "GetICloudpolicies") + } + for _, policy := range iPolicies { + existPolicies = append(existPolicies, policy.GetGlobalId()) + } + for _, policy := range policies { + if !utils.IsInStringArray(policy.ExternalId, existPolicies) { + err = iRole.AttachPolicy(policy.ExternalId, policy.PolicyType) + if err != nil { + return errors.Wrapf(err, "attach %s policy %s", policy.PolicyType, policy.ExternalId) + } + } + } + return nil } } iProvider, err := group.GetProvider() @@ -622,5 +653,14 @@ func (base SProviderBaseProviderDriver) RequestCreateRoleForSamlUser(ctx context user.CloudroleId = role.Id return nil }) - return err + if err != nil { + return err + } + for _, policy := range policies { + err := iRole.AttachPolicy(policy.ExternalId, policy.PolicyType) + if err != nil { + return errors.Wrapf(err, "attach %s policy %s", policy.PolicyType, policy.ExternalId) + } + } + return nil } diff --git a/pkg/cloudid/drivers/volcengine.go b/pkg/cloudid/drivers/volcengine.go new file mode 100644 index 0000000000..875908fef1 --- /dev/null +++ b/pkg/cloudid/drivers/volcengine.go @@ -0,0 +1,32 @@ +// 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. + +package drivers + +import ( + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudid/models" +) + +type SVolcEngineDriver struct { + SProviderBaseProviderDriver +} + +func (driver SVolcEngineDriver) GetProvider() string { + return api.CLOUD_PROVIDER_VOLCENGINE +} + +func init() { + models.RegisterProviderDriver(&SVolcEngineDriver{}) +} diff --git a/pkg/cloudid/models/cloudaccount.go b/pkg/cloudid/models/cloudaccount.go index 08e7c0566b..882cd8e205 100644 --- a/pkg/cloudid/models/cloudaccount.go +++ b/pkg/cloudid/models/cloudaccount.go @@ -18,7 +18,6 @@ import ( "context" "database/sql" "fmt" - "net/url" "strings" "yunion.io/x/cloudmux/pkg/cloudprovider" @@ -29,7 +28,6 @@ import ( "yunion.io/x/onecloud/pkg/apis" api "yunion.io/x/onecloud/pkg/apis/cloudid" - computeapi "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/cloudid/options" @@ -77,30 +75,6 @@ func (manager *SCloudaccountManager) GetResourceCount() ([]db.SScopeResourceCoun return []db.SScopeResourceCount{}, nil } -func (self *SCloudaccount) GetClouduserAccountName(name string) (string, string) { - account := "" - switch self.Provider { - case computeapi.CLOUD_PROVIDER_ALIYUN: - suffix := strings.TrimPrefix(self.IamLoginUrl, "https://signin.aliyun.com/") - suffix = strings.TrimSuffix(suffix, "/login.htm") - if len(suffix) > 0 { - name = fmt.Sprintf("%s@%s", name, suffix) - account = suffix - } - case computeapi.CLOUD_PROVIDER_QCLOUD, computeapi.CLOUD_PROVIDER_HUAWEI: - u, _ := url.Parse(self.IamLoginUrl) - if u != nil { - account = u.Query().Get("account") - } - case computeapi.CLOUD_PROVIDER_AWS: - account := strings.TrimPrefix(self.IamLoginUrl, "https://") - if info := strings.Split(account, "."); len(info) > 0 { - account = info[0] - } - } - return account, name -} - func (manager *SCloudaccountManager) GetCloudaccounts() ([]SCloudaccount, error) { accounts := []SCloudaccount{} q := manager.Query() diff --git a/pkg/cloudid/models/samldriver.go b/pkg/cloudid/models/samldriver.go index ed155bf66b..82276c408a 100644 --- a/pkg/cloudid/models/samldriver.go +++ b/pkg/cloudid/models/samldriver.go @@ -85,6 +85,7 @@ func GetMetadata(driver ICloudSAMLLoginDriver) ([]byte, error) { if err != nil { return nil, errors.Wrapf(err, "read body %s fail", metaUrl) } + os.WriteFile(filePath, metaBytes, 0644) } else { return nil, errors.Wrapf(err, "read file %s fail", filePath) } diff --git a/pkg/cloudid/saml/load.go b/pkg/cloudid/saml/load.go index 855cc7e7f4..e88bbd8010 100644 --- a/pkg/cloudid/saml/load.go +++ b/pkg/cloudid/saml/load.go @@ -22,4 +22,5 @@ import ( _ "yunion.io/x/onecloud/pkg/cloudid/saml/providers/google" _ "yunion.io/x/onecloud/pkg/cloudid/saml/providers/huawei" _ "yunion.io/x/onecloud/pkg/cloudid/saml/providers/qcloud" + _ "yunion.io/x/onecloud/pkg/cloudid/saml/providers/volcengine" ) diff --git a/pkg/cloudid/saml/providers/volcengine/doc.go b/pkg/cloudid/saml/providers/volcengine/doc.go new file mode 100644 index 0000000000..f70373a8c7 --- /dev/null +++ b/pkg/cloudid/saml/providers/volcengine/doc.go @@ -0,0 +1 @@ +package volcengine // import "yunion.io/x/onecloud/pkg/cloudid/saml/providers/volcengine" diff --git a/pkg/cloudid/saml/providers/volcengine/driver.go b/pkg/cloudid/saml/providers/volcengine/driver.go new file mode 100644 index 0000000000..e18511d109 --- /dev/null +++ b/pkg/cloudid/saml/providers/volcengine/driver.go @@ -0,0 +1,95 @@ +// 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. + +package volcengine + +import ( + "context" + "fmt" + + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/util/samlutils" + + "yunion.io/x/onecloud/pkg/cloudid/models" + "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/util/samlutils/idp" +) + +func (d *SVolcEngineSAMLDriver) GetIdpInitiatedLoginData(ctx context.Context, userCred mcclient.TokenCredential, managerId string, sp *idp.SSAMLServiceProvider, redirectUrl string) (samlutils.SSAMLIdpInitiatedLoginData, error) { + data := samlutils.SSAMLIdpInitiatedLoginData{} + + provider, err := models.CloudproviderManager.FetchProvier(managerId) + if err != nil { + return data, err + } + + account, err := provider.GetCloudaccount() + if err != nil { + return data, errors.Wrapf(err, "GetCloudaccount") + } + + role, err := provider.GetRole(ctx, userCred.GetUserId()) + if err != nil { + return data, err + } + + samlProvider, err := provider.GetSamlProvider() + if err != nil { + return data, err + } + + // trn:iam::${AccountID}:role/${RoleName},trn:iam::${AccountID}:saml-provider/${SAMLProviderName} + roleStr := fmt.Sprintf("trn:iam::%s:role/%s,trn:iam::%s:saml-provider/%s", account.AccountId, role.ExternalId, account.AccountId, samlProvider.ExternalId) + + data.NameId = role.Name + data.NameIdFormat = samlutils.NAME_ID_FORMAT_TRANSIENT + data.AudienceRestriction = "https://console.volcengine.com" + for _, v := range []struct { + name string + friendlyName string + value string + }{ + { + name: "https://www.volcengine.com/SAML/Attributes/Identity", + friendlyName: "RoleEntitlement", + value: roleStr, + }, + { + name: "https://www.volcengine.com/SAML/Attributes/SessionName", + friendlyName: "SessionName", + value: role.Name, + }, + { + name: "https://www.volcengine.com/SAML/Attributes/SessionDuration", + friendlyName: "SessionDuration", + value: "7200", + }, + } { + data.Attributes = append(data.Attributes, samlutils.SSAMLResponseAttribute{ + Name: v.name, + FriendlyName: v.friendlyName, + Values: []string{v.value}, + }) + } + if len(redirectUrl) == 0 { + redirectUrl = "https://console.volcengine.com" + } + data.RelayState = redirectUrl + + return data, nil +} + +func (d *SVolcEngineSAMLDriver) GetSpInitiatedLoginData(ctx context.Context, userCred mcclient.TokenCredential, managerId string, sp *idp.SSAMLServiceProvider) (samlutils.SSAMLSpInitiatedLoginData, error) { + return samlutils.SSAMLSpInitiatedLoginData{}, errors.ErrNotSupported +} diff --git a/pkg/cloudid/saml/providers/volcengine/factory.go b/pkg/cloudid/saml/providers/volcengine/factory.go new file mode 100644 index 0000000000..573a405a64 --- /dev/null +++ b/pkg/cloudid/saml/providers/volcengine/factory.go @@ -0,0 +1,39 @@ +// 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. + +package volcengine + +import ( + "yunion.io/x/cloudmux/pkg/cloudprovider" + + "yunion.io/x/onecloud/pkg/cloudid/models" +) + +type SVolcEngineSAMLDriver struct{} + +func (d *SVolcEngineSAMLDriver) GetEntityID() string { + return cloudprovider.SAML_ENTITY_ID_VOLC_ENGINE +} + +func (d *SVolcEngineSAMLDriver) GetMetadataFilename() string { + return "volcengine.xml" +} + +func (d *SVolcEngineSAMLDriver) GetMetadataUrl() string { + return "https://signin.volcengine.com/saml_role/SpMetadata.xml" +} + +func init() { + models.Register(&SVolcEngineSAMLDriver{}) +} diff --git a/pkg/cloudid/service/service.go b/pkg/cloudid/service/service.go index 387fdbd7b4..e1ad110a00 100644 --- a/pkg/cloudid/service/service.go +++ b/pkg/cloudid/service/service.go @@ -85,8 +85,8 @@ func StartService() { if !opts.IsSlaveNode { cron := cronman.InitCronJobManager(true, options.Options.CronJobWorkerCount) - cron.AddJobAtIntervalsWithStartRun("SyncCloudaccountResources", time.Duration(opts.CloudIdResourceSyncIntervalHours)*time.Hour, models.CloudaccountManager.SyncCloudaccountResources, false) - cron.AddJobAtIntervalsWithStartRun("SyncCloudproviderResources", time.Duration(opts.CloudIdResourceSyncIntervalHours)*time.Hour, models.CloudproviderManager.SyncCloudproviderResources, false) + cron.AddJobAtIntervalsWithStartRun("SyncCloudaccountResources", time.Duration(opts.CloudIdResourceSyncIntervalHours)*time.Hour, models.CloudaccountManager.SyncCloudaccountResources, true) + cron.AddJobAtIntervalsWithStartRun("SyncCloudproviderResources", time.Duration(opts.CloudIdResourceSyncIntervalHours)*time.Hour, models.CloudproviderManager.SyncCloudproviderResources, true) cron.AddJobEveryFewHour("AutoPurgeSplitable", 4, 30, 0, db.AutoPurgeSplitable, false) diff --git a/pkg/cloudid/tasks/cloudgroup_set_policies_task.go b/pkg/cloudid/tasks/cloudgroup_set_policies_task.go index b1f4d8ce40..404182d75e 100644 --- a/pkg/cloudid/tasks/cloudgroup_set_policies_task.go +++ b/pkg/cloudid/tasks/cloudgroup_set_policies_task.go @@ -80,7 +80,7 @@ func (self *CloudgroupSetPoliciesTask) OnInit(ctx context.Context, obj db.IStand for _, policy := range input.Add { for id, role := range iRoleMap { - err := role.AttachPolicy(policy.ExternalId) + err := role.AttachPolicy(policy.ExternalId, policy.PolicyType) if err != nil { logclient.AddSimpleActionLog(roleMap[id], logclient.ACT_ATTACH_POLICY, err, self.GetUserCred(), false) } @@ -99,7 +99,7 @@ func (self *CloudgroupSetPoliciesTask) OnInit(ctx context.Context, obj db.IStand for _, policy := range input.Del { for id, role := range iRoleMap { - err := role.DetachPolicy(policy.ExternalId) + err := role.DetachPolicy(policy.ExternalId, policy.PolicyType) if err != nil { logclient.AddSimpleActionLog(roleMap[id], logclient.ACT_DETACH_POLICY, err, self.GetUserCred(), false) } diff --git a/pkg/cloudid/tasks/clouduser_create_task.go b/pkg/cloudid/tasks/clouduser_create_task.go index d784efe15e..759591d650 100644 --- a/pkg/cloudid/tasks/clouduser_create_task.go +++ b/pkg/cloudid/tasks/clouduser_create_task.go @@ -82,9 +82,9 @@ func (self *ClouduserCreateTask) OnInit(ctx context.Context, obj db.IStandaloneM }{ Id: self.Id, IamLoginUrl: account.IamLoginUrl, + Account: account.AccountId, } msg.Password, _ = user.GetPassword() - msg.Account, msg.Name = account.GetClouduserAccountName(user.Name) notifyclient.NotifyWithContact(ctx, []string{user.Email}, npk.NotifyByEmail, npk.NotifyPriorityNormal, "CLOUD_USER_CREATED", jsonutils.Marshal(msg)) } diff --git a/vendor/modules.txt b/vendor/modules.txt index 7637d30daf..ec1a9a8056 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1472,7 +1472,7 @@ sigs.k8s.io/structured-merge-diff/v4/value # sigs.k8s.io/yaml v1.2.0 ## explicit; go 1.12 sigs.k8s.io/yaml -# yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240401032225-9a152ed52202 +# yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240402061507-4922cc681244 ## explicit; go 1.18 yunion.io/x/cloudmux/pkg/apis yunion.io/x/cloudmux/pkg/apis/billing diff --git a/vendor/yunion.io/x/cloudmux/pkg/cloudprovider/resources.go b/vendor/yunion.io/x/cloudmux/pkg/cloudprovider/resources.go index 63ce47791c..8fec6371ea 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/cloudprovider/resources.go +++ b/vendor/yunion.io/x/cloudmux/pkg/cloudprovider/resources.go @@ -1369,8 +1369,8 @@ type ICloudrole interface { GetSAMLProvider() string GetICloudpolicies() ([]ICloudpolicy, error) - AttachPolicy(id string) error - DetachPolicy(id string) error + AttachPolicy(policyName string, policyType string) error + DetachPolicy(policyName string, policyType string) error Delete() error } diff --git a/vendor/yunion.io/x/cloudmux/pkg/cloudprovider/saml.go b/vendor/yunion.io/x/cloudmux/pkg/cloudprovider/saml.go index ecd39c9bb4..490127a202 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/cloudprovider/saml.go +++ b/vendor/yunion.io/x/cloudmux/pkg/cloudprovider/saml.go @@ -24,9 +24,11 @@ const ( SAML_ENTITY_ID_HUAWEI_CLOUD = "https://auth.huaweicloud.com/" SAML_ENTITY_ID_GOOGLE = "google.com" SAML_ENTITY_ID_AZURE = "urn:federation:MicrosoftOnline" + SAML_ENTITY_ID_VOLC_ENGINE = "https://www.volcengine.com/" ) type SAMLProviderCreateOptions struct { Name string Metadata samlutils.EntityDescriptor + Desc string } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/aliyun/ram_role.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/aliyun/ram_role.go index ffc553b611..0056c9def2 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/aliyun/ram_role.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/aliyun/ram_role.go @@ -20,6 +20,7 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/utils" "yunion.io/x/cloudmux/pkg/cloudprovider" ) @@ -67,12 +68,12 @@ func (self *SRole) GetICloudpolicies() ([]cloudprovider.ICloudpolicy, error) { return ret, nil } -func (self *SRole) AttachPolicy(policyName string) error { - return self.client.AttachPolicy2Role("System", policyName, self.RoleName) +func (self *SRole) AttachPolicy(policyName string, policyType string) error { + return self.client.AttachPolicy2Role(utils.Capitalize(policyType), policyName, self.RoleName) } -func (self *SRole) DetachPolicy(policyName string) error { - return self.client.DetachPolicyFromRole("System", policyName, self.RoleName) +func (self *SRole) DetachPolicy(policyName string, policyType string) error { + return self.client.DetachPolicyFromRole(utils.Capitalize(policyType), policyName, self.RoleName) } func (self *SRole) Delete() error { diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/aws/iam_role.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/aws/iam_role.go index 1594dca0bb..2f1fa91093 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/aws/iam_role.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/aws/iam_role.go @@ -88,11 +88,11 @@ func (self *SRole) GetSAMLProvider() string { return "" } -func (self *SRole) AttachPolicy(id string) error { +func (self *SRole) AttachPolicy(id string, policyType string) error { return self.client.AttachRolePolicy(self.RoleName, self.client.getIamArn(id)) } -func (self *SRole) DetachPolicy(id string) error { +func (self *SRole) DetachPolicy(id string, polityType string) error { return self.client.DetachRolePolicy(self.RoleName, self.client.getIamArn(id)) } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/qcloud/cam_role.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/qcloud/cam_role.go index 739437a5cd..23a8903918 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/qcloud/cam_role.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/qcloud/cam_role.go @@ -128,11 +128,11 @@ func (self *SRole) GetICloudpolicies() ([]cloudprovider.ICloudpolicy, error) { return ret, nil } -func (self *SRole) AttachPolicy(id string) error { +func (self *SRole) AttachPolicy(id string, policyType string) error { return self.client.AttachRolePolicy(self.RoleName, id) } -func (self *SRole) DetachPolicy(id string) error { +func (self *SRole) DetachPolicy(id string, policyType string) error { return self.client.DetachRolePolicy(self.RoleName, id) } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/group.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/group.go new file mode 100644 index 0000000000..40113d0f8a --- /dev/null +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/group.go @@ -0,0 +1,298 @@ +// 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. + +package volcengine + +import ( + "fmt" + + "yunion.io/x/cloudmux/pkg/cloudprovider" +) + +type SGroup struct { + client *SVolcEngineClient + + Description string + CreatedDate string + UserGroupName string + UpdateDate string + AccountId string + UserGroupId string +} + +func (self *SGroup) GetName() string { + return self.UserGroupName +} + +func (self *SGroup) GetGlobalId() string { + return self.UserGroupName +} + +func (self *SGroup) GetDescription() string { + return self.Description +} + +func (self *SGroup) GetICloudusers() ([]cloudprovider.IClouduser, error) { + users, err := self.client.ListUsersForGroup(self.UserGroupName) + if err != nil { + return nil, err + } + ret := []cloudprovider.IClouduser{} + for i := range users { + users[i].client = self.client + ret = append(ret, &users[i]) + } + return ret, nil +} + +func (self *SGroup) GetISystemCloudpolicies() ([]cloudprovider.ICloudpolicy, error) { + policies, err := self.client.ListAttachedUserGroupPolicies(self.UserGroupName) + if err != nil { + return nil, err + } + ret := []cloudprovider.ICloudpolicy{} + for i := range policies { + policies[i].client = self.client + if policies[i].PolicyType == "System" { + ret = append(ret, &policies[i]) + } + } + return ret, nil +} + +func (self *SGroup) GetICustomCloudpolicies() ([]cloudprovider.ICloudpolicy, error) { + policies, err := self.client.ListAttachedUserGroupPolicies(self.UserGroupName) + if err != nil { + return nil, err + } + ret := []cloudprovider.ICloudpolicy{} + for i := range policies { + policies[i].client = self.client + if policies[i].PolicyType == "Custom" { + ret = append(ret, &policies[i]) + } + } + return ret, nil +} + +func (self *SGroup) AddUser(name string) error { + return self.client.AddUserToGroup(name, self.UserGroupName) +} + +func (self *SGroup) RemoveUser(name string) error { + return self.client.RemoveUserFromGroup(name, self.UserGroupName) +} + +func (self *SGroup) AttachSystemPolicy(policyName string) error { + return self.client.AttachUserGroupPolicy(self.UserGroupName, policyName, "System") +} + +func (self *SGroup) AttachCustomPolicy(policyName string) error { + return self.client.AttachUserGroupPolicy(self.UserGroupName, policyName, "Custom") +} + +func (self *SGroup) DetachSystemPolicy(policyName string) error { + return self.client.DetachUserGroupPolicy(self.UserGroupName, policyName, "System") +} + +func (self *SGroup) DetachCustomPolicy(policyName string) error { + return self.client.DetachUserGroupPolicy(self.UserGroupName, policyName, "Custom") +} + +func (self *SGroup) Delete() error { + return self.client.DeleteGroup(self.UserGroupName) +} + +func (self *SVolcEngineClient) CreateICloudgroup(name string, desc string) (cloudprovider.ICloudgroup, error) { + group, err := self.CreateGroup(name, desc) + if err != nil { + return nil, err + } + return group, nil +} + +func (self *SVolcEngineClient) GetICloudgroups() ([]cloudprovider.ICloudgroup, error) { + groups, err := self.ListGroups() + if err != nil { + return nil, err + } + ret := []cloudprovider.ICloudgroup{} + for i := range groups { + groups[i].client = self + ret = append(ret, &groups[i]) + } + return ret, nil +} + +func (client *SVolcEngineClient) ListGroups() ([]SGroup, error) { + params := map[string]string{ + "Limit": "50", + } + offset := 0 + ret := []SGroup{} + for { + params["Offset"] = fmt.Sprintf("%d", offset) + resp, err := client.iamRequest("", "ListGroups", params) + if err != nil { + return nil, err + } + part := struct { + UserGroups []SGroup + Total int + }{} + err = resp.Unmarshal(&part) + if err != nil { + return nil, err + } + ret = append(ret, part.UserGroups...) + if len(part.UserGroups) == 0 || len(ret) >= part.Total { + break + } + offset = len(ret) + } + return ret, nil +} + +func (client *SVolcEngineClient) ListUsersForGroup(name string) ([]SUser, error) { + params := map[string]string{ + "Limit": "50", + "UserGroupName": name, + } + offset := 0 + ret := []SUser{} + for { + params["Offset"] = fmt.Sprintf("%d", offset) + resp, err := client.iamRequest("", "ListUsersForGroup", params) + if err != nil { + return nil, err + } + part := struct { + Users []SUser + Total int + }{} + err = resp.Unmarshal(&part) + if err != nil { + return nil, err + } + ret = append(ret, part.Users...) + if len(part.Users) == 0 || len(ret) >= part.Total { + break + } + offset = len(ret) + } + return ret, nil +} + +func (client *SVolcEngineClient) ListAttachedUserGroupPolicies(name string) ([]SPolicy, error) { + params := map[string]string{ + "UserGroupName": name, + } + resp, err := client.iamRequest("", "ListAttachedUserGroupPolicies", params) + if err != nil { + return nil, err + } + ret := []SPolicy{} + err = resp.Unmarshal(&ret, "AttachedPolicyMetadata") + if err != nil { + return nil, err + } + return ret, nil +} + +func (client *SVolcEngineClient) AttachUserGroupPolicy(name, policy, policyType string) error { + params := map[string]string{ + "UserGroupName": name, + "PolicyName": policy, + "PolicyType": policyType, + } + _, err := client.iamRequest("", "AttachUserGroupPolicy", params) + return err +} + +func (client *SVolcEngineClient) DetachUserGroupPolicy(name, policy, policyType string) error { + params := map[string]string{ + "UserGroupName": name, + "PolicyName": policy, + "PolicyType": policyType, + } + _, err := client.iamRequest("", "DetachUserGroupPolicy", params) + return err +} + +func (client *SVolcEngineClient) DeleteGroup(name string) error { + params := map[string]string{ + "UserGroupName": name, + } + _, err := client.iamRequest("", "DeleteGroup", params) + return err +} + +func (client *SVolcEngineClient) AddUserToGroup(user, group string) error { + params := map[string]string{ + "UserGroupName": group, + "UserName": user, + } + _, err := client.iamRequest("", "AddUserToGroup", params) + return err +} + +func (client *SVolcEngineClient) RemoveUserFromGroup(user, group string) error { + params := map[string]string{ + "UserGroupName": group, + "UserName": user, + } + _, err := client.iamRequest("", "RemoveUserFromGroup", params) + return err +} + +func (client *SVolcEngineClient) CreateGroup(name, desc string) (*SGroup, error) { + params := map[string]string{ + "UserGroupName": name, + "Description": desc, + } + resp, err := client.iamRequest("", "CreateGroup", params) + if err != nil { + return nil, err + } + ret := &SGroup{client: client} + err = resp.Unmarshal(ret, "UserGroup") + if err != nil { + return nil, err + } + return ret, nil +} + +func (client *SVolcEngineClient) GetICloudgroupByName(name string) (cloudprovider.ICloudgroup, error) { + group, err := client.GetGroup(name) + if err != nil { + return nil, err + } + return group, nil +} + +func (client *SVolcEngineClient) GetGroup(name string) (*SGroup, error) { + params := map[string]string{ + "UserGroupName": name, + } + resp, err := client.iamRequest("", "GetGroup", params) + if err != nil { + return nil, err + } + ret := &SGroup{client: client} + err = resp.Unmarshal(ret, "UserGroup") + if err != nil { + return nil, err + } + return ret, nil +} diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/policy.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/policy.go new file mode 100644 index 0000000000..e811b56421 --- /dev/null +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/policy.go @@ -0,0 +1,143 @@ +// 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. + +package volcengine + +import ( + "fmt" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + + "yunion.io/x/cloudmux/pkg/cloudprovider" +) + +const ( + POLICY_TYPE_SYSTEM = "System" + POLICY_TYPE_CUSTOM = "Custom" +) + +type SPolicy struct { + client *SVolcEngineClient + + CreateDate string + UpdateDate string + PolicyDocument string + Status string + PolicyName string + PolicyType string + Description string + Category string + IsServiceRolePolicy int + AttachmentCount int +} + +func (policy *SPolicy) GetName() string { + return policy.PolicyName +} + +func (policy *SPolicy) GetDescription() string { + return policy.Description +} + +func (policy *SPolicy) GetGlobalId() string { + return policy.PolicyName +} + +func (policy *SPolicy) UpdateDocument(document *jsonutils.JSONDict) error { + return cloudprovider.ErrNotImplemented +} + +func (policy *SPolicy) Delete() error { + return policy.client.DeletePolicy(policy.PolicyName) +} + +func (policy *SPolicy) GetDocument() (*jsonutils.JSONDict, error) { + doc, err := jsonutils.Parse([]byte(policy.PolicyDocument)) + if err != nil { + return nil, err + } + ret, ok := doc.(*jsonutils.JSONDict) + if !ok { + return nil, errors.Wrapf(cloudprovider.ErrNotSupported, policy.PolicyDocument) + } + return ret, nil +} + +func (self *SVolcEngineClient) GetISystemCloudpolicies() ([]cloudprovider.ICloudpolicy, error) { + policies, err := self.ListPolicies("System") + if err != nil { + return nil, err + } + ret := []cloudprovider.ICloudpolicy{} + for i := range policies { + policies[i].client = self + ret = append(ret, &policies[i]) + } + return ret, nil +} + +func (self *SVolcEngineClient) GetICustomCloudpolicies() ([]cloudprovider.ICloudpolicy, error) { + policies, err := self.ListPolicies("Custom") + if err != nil { + return nil, err + } + ret := []cloudprovider.ICloudpolicy{} + for i := range policies { + policies[i].client = self + ret = append(ret, &policies[i]) + } + return ret, nil +} + +func (client *SVolcEngineClient) ListPolicies(scope string) ([]SPolicy, error) { + params := map[string]string{ + "Limit": "50", + } + if len(scope) > 0 { + params["Scope"] = scope + } + offset := 0 + ret := []SPolicy{} + for { + params["Offset"] = fmt.Sprintf("%d", offset) + resp, err := client.iamRequest("", "ListPolicies", params) + if err != nil { + return nil, err + } + part := struct { + PolicyMetadata []SPolicy + Total int + }{} + err = resp.Unmarshal(&part) + if err != nil { + return nil, err + } + ret = append(ret, part.PolicyMetadata...) + if len(part.PolicyMetadata) == 0 || len(ret) >= part.Total { + break + } + offset = len(ret) + } + return ret, nil + +} + +func (client *SVolcEngineClient) DeletePolicy(name string) error { + params := map[string]string{ + "PolicyName": name, + } + _, err := client.iamRequest("", "DeletePolicy", params) + return err +} diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/project.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/project.go index c93c2af782..f488f9a73c 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/project.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/project.go @@ -36,8 +36,8 @@ type SProject struct { Path string DisplayName string Description string - CreateDate time.Time - UpdateDate time.Time + CreateDate string + UpdateDate string Status string } @@ -77,7 +77,7 @@ func (client *SVolcEngineClient) GetProject(name string) (*SProject, error) { params := map[string]string{ "ProjectName": name, } - body, err := client.iamRequest("", "GetProject", params) + body, err := client.iam20210801Request("", "GetProject", params) if err != nil { return nil, err } @@ -97,7 +97,7 @@ func (client *SVolcEngineClient) ListProjects(limit int, offset int) ([]SProject "Limit": fmt.Sprintf("%d", limit), "Offset": fmt.Sprintf("%d", offset), } - resp, err := client.iamRequest("", "ListProjects", params) + resp, err := client.iam20210801Request("", "ListProjects", params) if err != nil { return nil, 0, errors.Wrap(err, "iamRequest.ListProjects") } @@ -123,7 +123,7 @@ func (client *SVolcEngineClient) CreateProject(name string) (*SProject, error) { "DisplayName": name, "ProjectName": name, } - resp, err := client.iamRequest("", "CreateProject", params) + resp, err := client.iam20210801Request("", "CreateProject", params) if err != nil { return nil, errors.Wrap(err, "CreateProject") } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/provider/provider.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/provider/provider.go index 25dc5a9506..8472ad7905 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/provider/provider.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/provider/provider.go @@ -16,6 +16,7 @@ package volcengine import ( "context" + "fmt" "strings" "yunion.io/x/pkg/errors" @@ -66,6 +67,10 @@ func (f *SVolcEngineProviderFactory) ValidateUpdateCloudaccountCredential(ctx co return output, nil } +func (factory *SVolcEngineProviderFactory) IsSupportSAMLAuth() bool { + return true +} + func validateClientCloudenv(client *volcengine.SVolcEngineClient) error { regions := client.GetIRegions() if len(regions) == 0 { @@ -210,3 +215,76 @@ func (self *SVolcEngineProvider) GetVersion() string { func (self *SVolcEngineProvider) GetMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) { return self.client.GetMetrics(opts) } + +func (self *SVolcEngineProvider) CreateICloudSAMLProvider(opts *cloudprovider.SAMLProviderCreateOptions) (cloudprovider.ICloudSAMLProvider, error) { + sp, err := self.client.CreateSAMLProvider(opts.Name, opts.Metadata.String(), opts.Desc) + if err != nil { + return nil, errors.Wrapf(err, "CreateSAMLProvider") + } + return sp, nil +} + +func (self *SVolcEngineProvider) GetICloudSAMLProviders() ([]cloudprovider.ICloudSAMLProvider, error) { + return self.client.GetICloudSAMLProviders() +} + +func (self *SVolcEngineProvider) GetICloudroles() ([]cloudprovider.ICloudrole, error) { + return self.client.GetICloudroles() +} + +func (self *SVolcEngineProvider) GetICloudroleById(id string) (cloudprovider.ICloudrole, error) { + role, err := self.client.GetRole(id) + if err != nil { + return nil, errors.Wrapf(err, "GetRole(%s)", id) + } + return role, nil +} + +func (self *SVolcEngineProvider) CreateICloudrole(opts *cloudprovider.SRoleCreateOptions) (cloudprovider.ICloudrole, error) { + stetement := fmt.Sprintf(`{"Statement":[{"Effect":"Allow","Action":["sts:AssumeRoleWithSAML"],"Principal":{"Federated":["trn:iam::%s:saml-provider/%s"]}}]}`, self.client.GetAccountId(), opts.SAMLProvider) + role, err := self.client.CreateRole(opts.Name, stetement, opts.Desc) + if err != nil { + return nil, errors.Wrapf(err, "CreateRole") + } + return role, nil +} + +func (self *SVolcEngineProvider) CreateIClouduser(conf *cloudprovider.SClouduserCreateConfig) (cloudprovider.IClouduser, error) { + return self.client.CreateIClouduser(conf) +} + +func (self *SVolcEngineProvider) GetICloudusers() ([]cloudprovider.IClouduser, error) { + return self.client.GetICloudusers() +} + +func (self *SVolcEngineProvider) GetIClouduserByName(name string) (cloudprovider.IClouduser, error) { + return self.client.GetIClouduserByName(name) +} + +func (self *SVolcEngineProvider) GetICloudgroups() ([]cloudprovider.ICloudgroup, error) { + return self.client.GetICloudgroups() +} + +func (self *SVolcEngineProvider) CreateICloudgroup(name, desc string) (cloudprovider.ICloudgroup, error) { + return self.client.CreateICloudgroup(name, desc) +} + +func (self *SVolcEngineProvider) GetICloudgroupByName(name string) (cloudprovider.ICloudgroup, error) { + return self.client.GetICloudgroupByName(name) +} + +func (self *SVolcEngineProvider) GetISystemCloudpolicies() ([]cloudprovider.ICloudpolicy, error) { + return self.client.GetISystemCloudpolicies() +} + +func (self *SVolcEngineProvider) GetICustomCloudpolicies() ([]cloudprovider.ICloudpolicy, error) { + return self.client.GetICustomCloudpolicies() +} + +func (self *SVolcEngineProvider) CreateICloudpolicy(opts *cloudprovider.SCloudpolicyCreateOptions) (cloudprovider.ICloudpolicy, error) { + return nil, cloudprovider.ErrNotImplemented +} + +func (self *SVolcEngineProvider) GetSamlEntityId() string { + return cloudprovider.SAML_ENTITY_ID_VOLC_ENGINE +} diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/role.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/role.go new file mode 100644 index 0000000000..0a648359fe --- /dev/null +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/role.go @@ -0,0 +1,224 @@ +// 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. + +package volcengine + +import ( + "fmt" + "strings" + + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/utils" + + "yunion.io/x/cloudmux/pkg/cloudprovider" +) + +type PolicyDocument struct { + Statement []struct { + Effect string + Action []string + Principal struct { + Federated []string + } + } +} + +type SRole struct { + client *SVolcEngineClient + + RoleName string + DisplayName string + TrustPolicyDocument string + Description string +} + +func (self *SRole) GetGlobalId() string { + return self.RoleName +} + +func (self *SRole) GetName() string { + return self.RoleName +} + +func (self *SRole) GetICloudpolicies() ([]cloudprovider.ICloudpolicy, error) { + policies, err := self.client.ListAttachedRolePolicies(self.RoleName) + if err != nil { + return nil, err + } + ret := []cloudprovider.ICloudpolicy{} + for i := range policies { + policies[i].client = self.client + ret = append(ret, &policies[i]) + } + return ret, nil +} + +func (self *SRole) AttachPolicy(policyName string, policyType string) error { + return self.client.AttachRolePolicy(self.RoleName, policyName, utils.Capitalize(policyType)) +} + +func (self *SRole) DetachPolicy(policyName string, policyType string) error { + return self.client.DetachRolePolicy(self.RoleName, policyName, utils.Capitalize(policyType)) +} + +func (self *SRole) Delete() error { + return self.client.DeleteRole(self.RoleName) +} + +func (self *SRole) GetDocument() *jsonutils.JSONDict { + doc, err := jsonutils.ParseString(self.TrustPolicyDocument) + if err != nil { + return nil + } + return doc.(*jsonutils.JSONDict) +} + +func (self *SRole) GetSAMLProvider() string { + document := self.GetDocument() + if document == nil { + return "" + } + info := &PolicyDocument{} + document.Unmarshal(info) + for _, statement := range info.Statement { + for _, sp := range statement.Principal.Federated { + info := strings.Split(sp, "/") + if len(info) == 2 { + return info[1] + } + } + } + return "" +} + +func (self *SVolcEngineClient) GetICloudroles() ([]cloudprovider.ICloudrole, error) { + roles, err := self.ListRoles() + if err != nil { + return nil, err + } + ret := []cloudprovider.ICloudrole{} + for i := range roles { + roles[i].client = self + ret = append(ret, &roles[i]) + } + return ret, nil +} + +func (client *SVolcEngineClient) ListRoles() ([]SRole, error) { + params := map[string]string{ + "Limit": "50", + } + offset := 0 + ret := []SRole{} + for { + params["Offset"] = fmt.Sprintf("%d", offset) + resp, err := client.iamRequest("", "ListRoles", params) + if err != nil { + return nil, err + } + part := struct { + RoleMetadata []SRole + Total int + }{} + err = resp.Unmarshal(&part) + if err != nil { + return nil, err + } + ret = append(ret, part.RoleMetadata...) + if len(part.RoleMetadata) == 0 || len(ret) >= part.Total { + break + } + offset = len(ret) + } + return ret, nil +} + +func (client *SVolcEngineClient) GetRole(name string) (*SRole, error) { + params := map[string]string{ + "RoleName": name, + } + resp, err := client.iamRequest("", "GetRole", params) + if err != nil { + return nil, err + } + ret := &SRole{client: client} + err = resp.Unmarshal(ret, "Role") + if err != nil { + return nil, err + } + return ret, nil +} + +func (client *SVolcEngineClient) CreateRole(name, statement, desc string) (*SRole, error) { + params := map[string]string{ + "RoleName": name, + "DisplayName": name, + "TrustPolicyDocument": statement, + "Description": desc, + } + resp, err := client.iamRequest("", "CreateRole", params) + if err != nil { + return nil, err + } + ret := &SRole{client: client} + err = resp.Unmarshal(ret, "Role") + if err != nil { + return nil, err + } + return ret, nil +} + +func (client *SVolcEngineClient) DeleteRole(name string) error { + params := map[string]string{ + "RoleName": name, + } + _, err := client.iamRequest("", "DeleteRole", params) + return err +} + +func (client *SVolcEngineClient) ListAttachedRolePolicies(name string) ([]SPolicy, error) { + params := map[string]string{ + "RoleName": name, + } + resp, err := client.iamRequest("", "ListAttachedRolePolicies", params) + if err != nil { + return nil, err + } + ret := []SPolicy{} + err = resp.Unmarshal(&ret, "AttachedPolicyMetadata") + if err != nil { + return nil, err + } + return ret, nil +} + +func (client *SVolcEngineClient) AttachRolePolicy(name, policy, policyType string) error { + params := map[string]string{ + "RoleName": name, + "PolicyName": policy, + "PolicyType": policyType, + } + _, err := client.iamRequest("", "AttachRolePolicy", params) + return err +} + +func (client *SVolcEngineClient) DetachRolePolicy(name, policy, policyType string) error { + params := map[string]string{ + "RoleName": name, + "PolicyName": policy, + "PolicyType": policyType, + } + _, err := client.iamRequest("", "DetachRolePolicy", params) + return err +} diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/saml_provider.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/saml_provider.go new file mode 100644 index 0000000000..a7f371c588 --- /dev/null +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/saml_provider.go @@ -0,0 +1,202 @@ +// Copyright 2023 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. + +package volcengine + +import ( + "encoding/base64" + "fmt" + "strings" + + "yunion.io/x/cloudmux/pkg/apis" + "yunion.io/x/cloudmux/pkg/apis/cloudid" + "yunion.io/x/cloudmux/pkg/cloudprovider" + "yunion.io/x/cloudmux/pkg/multicloud" + "yunion.io/x/jsonutils" + "yunion.io/x/pkg/errors" + "yunion.io/x/pkg/util/httputils" + "yunion.io/x/pkg/util/samlutils" +) + +type SSamlProvider struct { + multicloud.SResourceBase + multicloud.STagBase + client *SVolcEngineClient + + Trn string + EncodedSAMLMetadataDocument string + ProviderName string + SAMLProviderName string + IdpType string + SSOType string + Status string + Description string +} + +func (self *SSamlProvider) GetName() string { + if len(self.SAMLProviderName) > 0 { + return self.SAMLProviderName + } + return self.ProviderName +} + +func (self *SSamlProvider) GetGlobalId() string { + if len(self.SAMLProviderName) > 0 { + return self.SAMLProviderName + } + return self.ProviderName +} + +func (self *SSamlProvider) GetId() string { + if len(self.SAMLProviderName) > 0 { + return self.SAMLProviderName + } + return self.ProviderName +} + +func (self *SSamlProvider) GetAuthUrl(apiServer string) string { + input := samlutils.SIdpInitiatedLoginInput{ + EntityID: cloudprovider.SAML_ENTITY_ID_VOLC_ENGINE, + IdpId: self.client.cpcfg.AccountId, + } + return httputils.JoinPath(apiServer, cloudid.SAML_IDP_PREFIX, fmt.Sprintf("sso?%s", jsonutils.Marshal(input).QueryString())) +} + +func (self *SSamlProvider) Delete() error { + return self.client.DeleteSamlProvider(self.GetName()) +} + +func (self *SSamlProvider) GetStatus() string { + return apis.STATUS_AVAILABLE +} + +func (self *SSamlProvider) GetMetadataDocument() (*samlutils.EntityDescriptor, error) { + provider, err := self.client.GetSamlProvider(self.GetName()) + if err != nil { + return nil, err + } + data, err := base64.StdEncoding.DecodeString(provider.EncodedSAMLMetadataDocument) + if err != nil { + return nil, err + } + ret, err := samlutils.ParseMetadata(data) + if err != nil { + return nil, err + } + return &ret, nil +} + +func (self *SSamlProvider) UpdateMetadata(metadata samlutils.EntityDescriptor) error { + return self.client.UpdateSAMLProvider(self.GetName(), metadata.String()) +} + +func (self *SVolcEngineClient) GetSamlProviders() ([]SSamlProvider, error) { + params := map[string]string{} + params["Limit"] = "50" + ret := []SSamlProvider{} + offset := 0 + for { + params["Offset"] = fmt.Sprintf("%d", offset) + resp, err := self.iamRequest("", "ListSAMLProviders", params) + if err != nil { + return nil, err + } + part := struct { + SAMLProviders []SSamlProvider + Total int + }{} + err = resp.Unmarshal(&part) + if err != nil { + return nil, err + } + ret = append(ret, part.SAMLProviders...) + if len(ret) >= part.Total || len(part.SAMLProviders) == 0 { + break + } + offset = len(ret) + } + return ret, nil +} + +func (self *SVolcEngineClient) GetICloudSAMLProviders() ([]cloudprovider.ICloudSAMLProvider, error) { + providers, err := self.GetSamlProviders() + if err != nil { + return nil, err + } + ret := []cloudprovider.ICloudSAMLProvider{} + for i := range providers { + providers[i].client = self + ret = append(ret, &providers[i]) + } + return ret, nil +} + +func (self *SVolcEngineClient) DeleteSamlProvider(name string) error { + params := map[string]string{ + "SAMLProviderName": name, + } + _, err := self.iamRequest("", "DeleteSAMLProvider", params) + return err +} + +func (self *SVolcEngineClient) CreateSAMLProvider(name, metadata, desc string) (*SSamlProvider, error) { + name = strings.ReplaceAll(name, ":", "_") + params := map[string]string{ + "EncodedSAMLMetadataDocument": base64.StdEncoding.EncodeToString([]byte(metadata)), + "SAMLProviderName": name, + "SSOType": "1", + "Status": "1", + } + if len(desc) > 0 { + params["Description"] = desc + } + resp, err := self.iamRequest("", "CreateSAMLProvider", params) + if err != nil { + return nil, errors.Wrapf(err, "CreateSAMLProvider") + } + sp := &SSamlProvider{client: self} + err = resp.Unmarshal(sp) + if err != nil { + return nil, errors.Wrapf(err, "resp.Unmarshal") + } + return sp, nil +} + +func (self *SVolcEngineClient) GetSamlProvider(name string) (*SSamlProvider, error) { + params := map[string]string{ + "SAMLProviderName": name, + } + resp, err := self.iamRequest("", "GetSAMLProvider", params) + if err != nil { + return nil, err + } + ret := &SSamlProvider{client: self} + err = resp.Unmarshal(ret) + if err != nil { + return nil, err + } + return ret, nil + +} + +func (self *SVolcEngineClient) UpdateSAMLProvider(name, metadata string) error { + name = strings.ReplaceAll(name, ":", "_") + params := map[string]string{ + "EncodedSAMLMetadataDocument": base64.StdEncoding.EncodeToString([]byte(metadata)), + "SAMLProviderName": name, + "Status": "1", + } + _, err := self.iamRequest("", "UpdateSAMLProvider", params) + return err +} diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/user.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/user.go index 62b9dce45c..d8b9a7a3cd 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/user.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/user.go @@ -15,15 +15,20 @@ package volcengine import ( - "time" + "fmt" + "yunion.io/x/cloudmux/pkg/cloudprovider" + "yunion.io/x/cloudmux/pkg/multicloud" "yunion.io/x/pkg/errors" ) type SUser struct { + multicloud.SBaseClouduser + client *SVolcEngineClient + Id int - CreateDate time.Time - UpdateDate time.Time + CreateDate string + UpdateDate string Status string AccountId string UserName string @@ -37,27 +42,310 @@ type SUser struct { Source string } -type SCallerIdentity struct { - AccountId string - UserId string - RoleId string - PrincipalId string - IdentityType string +func (user *SUser) GetGlobalId() string { + return user.UserName } -func (client *SVolcEngineClient) GetCallerIdentity() (*SCallerIdentity, error) { - // sys is not currently supported - params := map[string]string{} - body, err := client.iamRequest("", "ListUsers", params) +func (user *SUser) GetName() string { + return user.UserName +} + +func (user *SUser) GetEmailAddr() string { + return user.Email +} + +func (user *SUser) GetInviteUrl() string { + return "" +} + +func (user *SUser) Delete() error { + return user.client.DeleteUser(user.UserName) +} + +func (user *SUser) GetICloudgroups() ([]cloudprovider.ICloudgroup, error) { + groups, err := user.client.ListGroupsForUser(user.UserName) if err != nil { return nil, err } - id := &SCallerIdentity{} - users := []SUser{} - err = body.Unmarshal(&users, "UserMetadata") - if err != nil { - return nil, errors.Wrap(err, "resp.Unmarshal") + ret := []cloudprovider.ICloudgroup{} + for i := range groups { + groups[i].client = user.client + ret = append(ret, &groups[i]) } - id.AccountId = users[0].AccountId - return id, nil + return ret, nil +} + +func (user *SUser) GetISystemCloudpolicies() ([]cloudprovider.ICloudpolicy, error) { + policies, err := user.client.ListAttachedUserPolicies(user.UserName) + if err != nil { + return nil, err + } + ret := []cloudprovider.ICloudpolicy{} + for i := range policies { + policies[i].client = user.client + if policies[i].PolicyType == "System" { + ret = append(ret, &policies[i]) + } + } + return ret, nil +} + +func (user *SUser) GetICustomCloudpolicies() ([]cloudprovider.ICloudpolicy, error) { + policies, err := user.client.ListAttachedUserPolicies(user.UserName) + if err != nil { + return nil, err + } + ret := []cloudprovider.ICloudpolicy{} + for i := range policies { + policies[i].client = user.client + if policies[i].PolicyType == "Custom" { + ret = append(ret, &policies[i]) + } + } + return ret, nil +} + +func (user *SUser) IsConsoleLogin() bool { + profile, err := user.client.GetLoginProfile(user.UserName) + if err != nil { + return false + } + return profile.LoginAllowed +} + +func (user *SUser) ResetPassword(password string) error { + return user.client.UpdateLoginProfile(user.UserName, password, nil) +} + +func (user *SUser) AttachSystemPolicy(policyName string) error { + return user.client.AttachUserPolicy(user.UserName, policyName, "System") +} + +func (user *SUser) AttachCustomPolicy(policyName string) error { + return user.client.AttachUserPolicy(user.UserName, policyName, "Custom") +} + +func (user *SUser) DetachSystemPolicy(policyName string) error { + return user.client.DetachUserPolicy(user.UserName, policyName, "System") +} + +func (user *SUser) DetachCustomPolicy(policyName string) error { + return user.client.DetachUserPolicy(user.UserName, policyName, "Custom") +} + +func (client *SVolcEngineClient) GetUsers() ([]SUser, error) { + params := map[string]string{ + "Limit": "50", + } + offset := 0 + ret := []SUser{} + for { + params["Offset"] = fmt.Sprintf("%d", offset) + resp, err := client.iamRequest("", "ListUsers", params) + if err != nil { + return nil, err + } + part := struct { + UserMetadata []SUser + Total int + }{} + err = resp.Unmarshal(&part) + if err != nil { + return nil, err + } + ret = append(ret, part.UserMetadata...) + if len(part.UserMetadata) == 0 || len(ret) >= part.Total { + break + } + offset = len(ret) + } + return ret, nil +} + +func (self *SVolcEngineClient) DeleteUser(name string) error { + params := map[string]string{ + "UserName": name, + } + _, err := self.iamRequest("", "DeleteUser", params) + return err +} + +func (client *SVolcEngineClient) GetICloudusers() ([]cloudprovider.IClouduser, error) { + users, err := client.GetUsers() + if err != nil { + return nil, err + } + ret := []cloudprovider.IClouduser{} + for i := range users { + users[i].client = client + ret = append(ret, &users[i]) + } + return ret, nil +} + +func (client *SVolcEngineClient) CreateIClouduser(opts *cloudprovider.SClouduserCreateConfig) (cloudprovider.IClouduser, error) { + user, err := client.CreateUser(opts) + if err != nil { + return nil, err + } + err = client.CreateLoginProfile(user.UserName, opts.Password, &opts.IsConsoleLogin) + if err != nil { + return nil, errors.Wrapf(err, "CreateLoginProfile") + } + return user, nil +} + +func (self *SVolcEngineClient) CreateUser(opts *cloudprovider.SClouduserCreateConfig) (*SUser, error) { + params := map[string]string{ + "UserName": opts.Name, + "Description": opts.Desc, + "Email": opts.Email, + "MobilePhone": opts.MobilePhone, + } + resp, err := self.iamRequest("", "CreateUser", params) + if err != nil { + return nil, err + } + ret := &SUser{client: self} + err = resp.Unmarshal(ret, "User") + if err != nil { + return nil, err + } + return ret, nil +} + +type LoginProfile struct { + PasswordResetRequired bool + LoginAllowed bool + LastLoginDate string +} + +func (self *SVolcEngineClient) GetLoginProfile(name string) (*LoginProfile, error) { + params := map[string]string{ + "UserName": name, + } + resp, err := self.iamRequest("", "GetLoginProfile", params) + if err != nil { + return nil, err + } + ret := &LoginProfile{} + err = resp.Unmarshal(ret, "LoginProfile") + if err != nil { + return nil, err + } + return ret, nil +} + +func (self *SVolcEngineClient) CreateLoginProfile(name, password string, loginAllowd *bool) error { + params := map[string]string{ + "UserName": name, + "Password": password, + } + if loginAllowd != nil { + params["LoginAllowed"] = fmt.Sprintf("%v", *loginAllowd) + } + _, err := self.iamRequest("", "CreateLoginProfile", params) + return err +} + +func (self *SVolcEngineClient) UpdateLoginProfile(name, password string, loginAllowd *bool) error { + params := map[string]string{ + "UserName": name, + "Password": password, + } + if loginAllowd != nil { + params["LoginAllowed"] = fmt.Sprintf("%v", *loginAllowd) + } + _, err := self.iamRequest("", "UpdateLoginProfile", params) + return err +} + +func (client *SVolcEngineClient) ListGroupsForUser(name string) ([]SGroup, error) { + params := map[string]string{ + "Limit": "50", + "UserName": name, + } + offset := 0 + ret := []SGroup{} + for { + params["Offset"] = fmt.Sprintf("%d", offset) + resp, err := client.iamRequest("", "ListGroupsForUser", params) + if err != nil { + return nil, err + } + part := struct { + UserGroups []SGroup + Total int + }{} + err = resp.Unmarshal(&part) + if err != nil { + return nil, err + } + ret = append(ret, part.UserGroups...) + if len(part.UserGroups) == 0 || len(ret) >= part.Total { + break + } + offset = len(ret) + } + return ret, nil +} + +func (client *SVolcEngineClient) ListAttachedUserPolicies(name string) ([]SPolicy, error) { + params := map[string]string{ + "UserName": name, + } + resp, err := client.iamRequest("", "ListAttachedUserPolicies", params) + if err != nil { + return nil, err + } + ret := []SPolicy{} + err = resp.Unmarshal(&ret, "AttachedPolicyMetadata") + if err != nil { + return nil, err + } + return ret, nil +} + +func (client *SVolcEngineClient) AttachUserPolicy(name, policy, policyType string) error { + params := map[string]string{ + "UserName": name, + "PolicyName": policy, + "PolicyType": policyType, + } + _, err := client.iamRequest("", "AttachUserPolicy", params) + return err +} + +func (client *SVolcEngineClient) DetachUserPolicy(name, policy, policyType string) error { + params := map[string]string{ + "UserName": name, + "PolicyName": policy, + "PolicyType": policyType, + } + _, err := client.iamRequest("", "DetachUserPolicy", params) + return err +} + +func (client *SVolcEngineClient) GetIClouduserByName(name string) (cloudprovider.IClouduser, error) { + user, err := client.GetUser(name) + if err != nil { + return nil, err + } + return user, nil +} + +func (client *SVolcEngineClient) GetUser(name string) (*SUser, error) { + params := map[string]string{ + "UserName": name, + } + resp, err := client.iamRequest("", "GetUser", params) + if err != nil { + return nil, err + } + ret := &SUser{client: client} + err = resp.Unmarshal(ret, "User") + if err != nil { + return nil, err + } + return ret, nil } diff --git a/vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/volcengine.go b/vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/volcengine.go index 9154517e3b..4c3fa8f577 100644 --- a/vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/volcengine.go +++ b/vendor/yunion.io/x/cloudmux/pkg/multicloud/volcengine/volcengine.go @@ -18,14 +18,18 @@ import ( "context" "crypto/tls" "fmt" + "io" "net/http" + "net/http/httputil" "net/url" "strings" "sync" "time" + "github.com/fatih/color" tos "github.com/volcengine/ve-tos-golang-sdk/v2/tos" sdk "github.com/volcengine/volc-sdk-golang/base" + "moul.io/http2curl/v2" "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/errors" @@ -42,7 +46,7 @@ const ( CLOUD_PROVIDER_VOLCENGINE_EN = "VolcEngine" VOLCENGINE_API_VERSION = "2020-04-01" - VOLCENGINE_IAM_API_VERSION = "2021-08-01" + VOLCENGINE_IAM_API_VERSION = "2018-01-01" VOLCENGINE_OBSERVE_API_VERSION = "2018-01-01" VOLCENGINE_BILLING_API_VERSION = "2022-01-01" @@ -98,10 +102,6 @@ func (cfg *VolcEngineClientConfig) Debug(debug bool) *VolcEngineClientConfig { return cfg } -func (cfg VolcEngineClientConfig) Copy() VolcEngineClientConfig { - return cfg -} - type SVolcEngineClient struct { *VolcEngineClientConfig @@ -180,11 +180,16 @@ func (client *SVolcEngineClient) GetAccountId() string { if len(client.ownerId) > 0 { return client.ownerId } - caller, err := client.GetCallerIdentity() + + toscli, err := client.getTosClient(VOLCENGINE_DEFAULT_REGION) if err != nil { return "" } - client.ownerId = caller.AccountId + out, err := toscli.ListBuckets(context.Background(), &tos.ListBucketsInput{}) + if err != nil { + return "" + } + client.ownerId = out.Owner.ID return client.ownerId } @@ -229,7 +234,7 @@ func (client *SVolcEngineClient) GetProjects() ([]SProject, error) { if len(client.projects) >= total { break } - offset += total + offset = len(client.projects) } return client.projects, nil } @@ -287,19 +292,6 @@ func (client *SVolcEngineClient) jsonRequest(cred sdk.Credentials, domain string "Version": []string{apiVersion}, } - var body interface{} = nil - if _params, ok := params.(map[string]string); ok { - for k, v := range _params { - query.Set(k, v) - } - } else { - body = params - } - - u, err := url.Parse(fmt.Sprintf("http://%s?%s", domain, query.Encode())) - if err != nil { - return nil, errors.Wrapf(err, "url.Parse") - } method := httputils.GET for prefix, _method := range map[string]httputils.THttpMethod{ "Get": httputils.GET, @@ -307,6 +299,7 @@ func (client *SVolcEngineClient) jsonRequest(cred sdk.Credentials, domain string "List": httputils.GET, "Delete": httputils.GET, "Put": httputils.PUT, + "Create": httputils.POST, } { if strings.HasPrefix(apiName, prefix) { method = _method @@ -322,21 +315,84 @@ func (client *SVolcEngineClient) jsonRequest(cred sdk.Credentials, domain string method = httputils.POST } - req := httputils.NewJsonRequest(method, u.String(), body) - vErr := &sVolcError{} - _cli := &sCred{ + form := url.Values{} + _params, _ := params.(map[string]string) + switch method { + case httputils.POST: + for k, v := range _params { + form.Set(k, v) + query.Set(k, v) + } + default: + for k, v := range _params { + query.Set(k, v) + } + } + + u, err := url.Parse(fmt.Sprintf("http://%s?%s", domain, query.Encode())) + if err != nil { + return nil, errors.Wrapf(err, "url.Parse") + } + + req, err := http.NewRequest(string(method), u.String(), strings.NewReader(form.Encode())) + if err != nil { + return nil, errors.Wrapf(err, "NewRequest") + } + + cli := &sCred{ client: client, cred: cred, } - cli := httputils.NewJsonClient(_cli) - _, resp, err := cli.Send(context.Background(), req, vErr, client.debug) + + resp, err := cli.Do(req) if err != nil { - return nil, errors.Wrapf(err, apiName) + return nil, errors.Wrapf(err, "Do request") } - if resp.Contains("Result") { - return resp.Get("Result") + defer resp.Body.Close() + + red := color.New(color.FgRed, color.Bold).PrintlnFunc() + green := color.New(color.FgGreen, color.Bold).PrintlnFunc() + yellow := color.New(color.FgYellow, color.Bold).PrintlnFunc() + cyan := color.New(color.FgHiCyan, color.Bold).PrintlnFunc() + + if client.debug { + dump, _ := httputil.DumpRequestOut(req, true) + yellow(string(dump)) + if req.Header.Get("Content-Type") != "application/octet-stream" { + curlCmd, _ := http2curl.GetCurlCommand(req) + cyan("CURL:", curlCmd, "\n") + } + + dump, _ = httputil.DumpResponse(resp, true) + if resp.StatusCode < 300 { + green(string(dump)) + } else if resp.StatusCode < 400 { + yellow(string(dump)) + } else { + red(string(dump)) + } } - return resp, nil + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrapf(err, "Read body") + } + + obj, err := jsonutils.Parse(body) + if err != nil { + return nil, errors.Wrapf(err, "Parse body %s", string(body)) + } + + // {"ResponseMetadata":{"RequestId":"202404021200176E2994C9222303CC731B","Action":"CreateUser","Version":"2018-01-01","Service":"iam","Region":"cn-beijing","Error":{"Code":"ParameterNotFound","Message":"The parameter 'UserName' is required."}}} + if obj.Contains("ResponseMetadata", "Error") { + ve := &sVolcError{StatusCode: resp.StatusCode} + obj.Unmarshal(ve, "ResponseMetadata") + return nil, ve + } + + if obj.Contains("Result") { + return obj.Get("Result") + } + return obj, nil } func (client *SVolcEngineClient) getSdkCredential(region string, service string, token string) sdk.Credentials { @@ -381,19 +437,16 @@ func (self *sVolcError) Error() string { return jsonutils.Marshal(self).String() } -func (self *sVolcError) ParseErrorFromJsonResponse(statusCode int, status string, body jsonutils.JSONObject) error { - if body != nil { - body.Unmarshal(self, "ResponseMetadata") - } - self.StatusCode = statusCode - return self -} - func (client *SVolcEngineClient) ecsRequest(region string, apiName string, params map[string]string) (jsonutils.JSONObject, error) { cred := client.getDefaultCredential(region, VOLCENGINE_SERVICE_ECS) return client.jsonRequest(cred, VOLCENGINE_API, VOLCENGINE_API_VERSION, apiName, params) } +func (client *SVolcEngineClient) iam20210801Request(region string, apiName string, params map[string]string) (jsonutils.JSONObject, error) { + cred := client.getDefaultCredential(region, VOLCENGINE_SERVICE_IAM) + return client.jsonRequest(cred, VOLCENGINE_IAM_API, "2021-08-01", apiName, params) +} + func (client *SVolcEngineClient) iamRequest(region string, apiName string, params map[string]string) (jsonutils.JSONObject, error) { cred := client.getDefaultCredential(region, VOLCENGINE_SERVICE_IAM) return client.jsonRequest(cred, VOLCENGINE_IAM_API, VOLCENGINE_IAM_API_VERSION, apiName, params) @@ -477,6 +530,8 @@ func (region *SVolcEngineClient) GetCapabilities() []string { cloudprovider.CLOUD_CAPABILITY_NETWORK, cloudprovider.CLOUD_CAPABILITY_SECURITY_GROUP, cloudprovider.CLOUD_CAPABILITY_EIP, + cloudprovider.CLOUD_CAPABILITY_CLOUDID, + cloudprovider.CLOUD_CAPABILITY_SAML_AUTH, cloudprovider.CLOUD_CAPABILITY_OBJECTSTORE, } return caps