diff --git a/cmd/account-server/service/account-set/root-account/check.go b/cmd/account-server/service/account-set/root-account/check.go index 0bf98b33b0..f86d2974bd 100644 --- a/cmd/account-server/service/account-set/root-account/check.go +++ b/cmd/account-server/service/account-set/root-account/check.go @@ -20,12 +20,19 @@ package rootaccount import ( + "errors" "fmt" + accountset "hcm/pkg/api/account-server/account-set" + "hcm/pkg/api/cloud-server/account" "hcm/pkg/api/core" + "hcm/pkg/api/core/cloud" "hcm/pkg/client" "hcm/pkg/criteria/enumor" + "hcm/pkg/criteria/errf" "hcm/pkg/dal/dao/tools" + "hcm/pkg/iam/meta" + "hcm/pkg/logs" "hcm/pkg/rest" ) @@ -54,3 +61,123 @@ func CheckDuplicateRootAccount(cts *rest.Contexts, client *client.ClientSet, ven return nil } + +// QueryRootAccountBySecret 根据秘钥获取账号信息 +func (s *service) QueryRootAccountBySecret(cts *rest.Contexts) (interface{}, error) { + vendor := enumor.Vendor(cts.Request.PathParameter("vendor")) + if err := vendor.Validate(); err != nil { + return nil, errf.NewFromErr(errf.InvalidParameter, err) + } + + // 校验用户有一级账号管理权限 + if err := s.checkPermission(cts, meta.RootAccount, meta.Find); err != nil { + return nil, err + } + + switch vendor { + case enumor.HuaWei: + return s.getHuaWeiAccountInfo(cts) + case enumor.Aws: + return s.getAwsAccountInfo(cts) + case enumor.Azure: + return s.getAzureAccountInfo(cts) + case enumor.Gcp: + return s.getGcpAccountInfo(cts) + default: + return nil, fmt.Errorf("unsupported vendor: %s, for get root account info", vendor) + } +} + +func (s *service) getHuaWeiAccountInfo(cts *rest.Contexts) (*cloud.HuaWeiInfoBySecret, error) { + req := new(accountset.HuaWeiAccountInfoBySecretReq) + if err := cts.DecodeInto(req); err != nil { + return nil, errf.NewFromErr(errf.DecodeRequestFailed, err) + } + if err := req.Validate(); err != nil { + return nil, errf.NewFromErr(errf.InvalidParameter, err) + } + + info, err := s.client.HCService().HuaWei.Account.GetBySecret(cts.Kit.Ctx, cts.Kit.Header(), req.HuaWeiSecret) + if err != nil { + logs.Errorf("fail to get huawei account info, err: %v, rid: %s", err, cts.Kit.Rid) + return nil, err + } + + return info, nil +} + +func (s *service) getAwsAccountInfo(cts *rest.Contexts) (*cloud.AwsInfoBySecret, error) { + req := new(accountset.AwsAccountInfoBySecretReq) + if err := cts.DecodeInto(req); err != nil { + return nil, errf.NewFromErr(errf.DecodeRequestFailed, err) + } + if err := req.Validate(); err != nil { + return nil, errf.NewFromErr(errf.InvalidParameter, err) + } + + info, err := s.client.HCService().Aws.Account.GetBySecret(cts.Kit.Ctx, cts.Kit.Header(), req.AwsSecret) + if err != nil { + logs.Errorf("fail to get aws account info, err: %v, rid: %s", err, cts.Kit.Rid) + return nil, err + } + + return info, nil +} + +func (s *service) getGcpAccountInfo(cts *rest.Contexts) ([]cloud.GcpProjectInfo, error) { + req := new(accountset.GcpAccountInfoBySecretReq) + if err := cts.DecodeInto(req); err != nil { + return nil, errf.NewFromErr(errf.DecodeRequestFailed, err) + } + if err := req.Validate(); err != nil { + return nil, errf.NewFromErr(errf.InvalidParameter, err) + } + + info, err := s.client.HCService().Gcp.Account.GetBySecret(cts.Kit.Ctx, cts.Kit.Header(), req.GcpSecret) + if err != nil { + logs.Errorf("fail to get gcp account info, err: %v, rid: %s", err, cts.Kit.Rid) + return nil, err + } + + return info.CloudProjectInfos, nil +} + +func (s *service) getAzureAccountInfo(cts *rest.Contexts) (*account.AzureAccountInfoBySecretResp, error) { + req := new(accountset.AzureAccountInfoBySecretReq) + if err := cts.DecodeInto(req); err != nil { + return nil, errf.NewFromErr(errf.DecodeRequestFailed, err) + } + if err := req.Validate(); err != nil { + return nil, errf.NewFromErr(errf.InvalidParameter, err) + } + + info, err := s.client.HCService().Azure.Account.GetBySecret(cts.Kit.Ctx, cts.Kit.Header(), req.AzureSecret) + if err != nil { + logs.Errorf("fail to get azure account info, err: %v, rid: %s", err, cts.Kit.Rid) + return nil, err + } + if len(info.SubscriptionInfos) < 1 { + logs.Errorf("get azure account info failed, no subscription found, rid: %s", cts.Kit.Rid) + return nil, errors.New("no subscription found") + } + + subscription := info.SubscriptionInfos[0] + result := &account.AzureAccountInfoBySecretResp{ + CloudSubscriptionID: subscription.CloudSubscriptionID, + CloudSubscriptionName: subscription.CloudSubscriptionName, + } + // 补全ApplicationName + for _, one := range info.ApplicationInfos { + if one.CloudApplicationID == req.CloudApplicationID { + result.CloudApplicationName = one.CloudApplicationName + break + } + } + // 没有拿到应用id的情况 + if len(result.CloudApplicationName) == 0 { + logs.Errorf("failed to get application name, rid: %s", cts.Kit.Rid) + return nil, fmt.Errorf("failed to get application name") + } + + return result, nil +} diff --git a/cmd/account-server/service/account-set/root-account/service.go b/cmd/account-server/service/account-set/root-account/service.go index b56c62935c..fc23acfee1 100644 --- a/cmd/account-server/service/account-set/root-account/service.go +++ b/cmd/account-server/service/account-set/root-account/service.go @@ -48,6 +48,9 @@ func InitService(c *capability.Capability) { h.Add("UpdateRootAccount", http.MethodPatch, "/root_accounts/{account_id}", svc.Update) h.Add("AddRootAccount", http.MethodPost, "/root_accounts/add", svc.Add) + h.Add("QueryRootAccountBySecret", http.MethodPost, "/vendors/{vendor}/root_accounts/query_account_by_secret", + svc.QueryRootAccountBySecret) + h.Load(c.WebService) } diff --git a/cmd/cloud-server/service/account/get.go b/cmd/cloud-server/service/account/get.go index f63c0effea..00a5374081 100644 --- a/cmd/cloud-server/service/account/get.go +++ b/cmd/cloud-server/service/account/get.go @@ -21,6 +21,7 @@ package account import ( "fmt" + "strings" "hcm/pkg/api/cloud-server/account" "hcm/pkg/api/core/cloud" @@ -198,7 +199,7 @@ func (a *accountSvc) getAndCheckAwsAccountInfo(cts *rest.Contexts) (*cloud.AwsIn return info, nil } -func (a *accountSvc) getAndCheckAzureAccountInfo(cts *rest.Contexts) (*cloud.AzureInfoBySecret, error) { +func (a *accountSvc) getAndCheckAzureAccountInfo(cts *rest.Contexts) (*account.AzureAccountInfoBySecretResp, error) { req := new(account.AzureAccountInfoBySecretReq) if err := cts.DecodeInto(req); err != nil { return nil, errf.NewFromErr(errf.DecodeRequestFailed, err) @@ -212,18 +213,46 @@ func (a *accountSvc) getAndCheckAzureAccountInfo(cts *rest.Contexts) (*cloud.Azu logs.Errorf("fail to get account info, err: %v, rid: %s", err, cts.Kit.Rid) return nil, err } + + // 校验订阅数量,要求订阅数量刚好一个 + if len(info.SubscriptionInfos) > 1 { + subs := make([]string, len(info.SubscriptionInfos)) + for i, sub := range info.SubscriptionInfos { + subs[i] = "(" + sub.CloudSubscriptionID + ")" + sub.CloudSubscriptionName + } + logs.Errorf("more than one subscription found: %s, rid: %s", strings.Join(subs, ","), cts.Kit.Rid) + return nil, fmt.Errorf("more than one subscription found: %s", strings.Join(subs, ",")) + } + subscription := info.SubscriptionInfos[0] + result := &account.AzureAccountInfoBySecretResp{ + CloudSubscriptionID: subscription.CloudSubscriptionID, + CloudSubscriptionName: subscription.CloudSubscriptionName, + } + // 补全ApplicationName + for _, one := range info.ApplicationInfos { + if one.CloudApplicationID == req.CloudApplicationID { + result.CloudApplicationName = one.CloudApplicationName + break + } + } + // 没有拿到应用id的情况 + if len(result.CloudApplicationName) == 0 { + logs.Errorf("failed to get application name, rid: %s", cts.Kit.Rid) + return nil, fmt.Errorf("failed to get application name") + } + if req.DisableCheck { - return info, nil + return result, nil } if err = CheckDuplicateMainAccount(cts, a.client, enumor.Azure, enumor.ResourceAccount, - info.CloudSubscriptionID); err != nil { + subscription.CloudSubscriptionID); err != nil { logs.Errorf("check whether main account duplicate fail, err: %v, rid: %s", err, cts.Kit.Rid) return nil, err } - return info, nil + return result, nil } -func (a *accountSvc) getAndCheckGcpAccountInfo(cts *rest.Contexts) (*cloud.GcpInfoBySecret, error) { +func (a *accountSvc) getAndCheckGcpAccountInfo(cts *rest.Contexts) (*cloud.GcpProjectInfo, error) { req := new(account.GcpAccountInfoBySecretReq) if err := cts.DecodeInto(req); err != nil { return nil, errf.NewFromErr(errf.DecodeRequestFailed, err) @@ -237,15 +266,28 @@ func (a *accountSvc) getAndCheckGcpAccountInfo(cts *rest.Contexts) (*cloud.GcpIn logs.Errorf("fail to get account info, err: %v, rid: %s", err, cts.Kit.Rid) return nil, err } + + // 只能有一个对应的project,多project报错。 + if len(info.CloudProjectInfos) > 1 { + projects := make([]string, len(info.CloudProjectInfos)) + for i, project := range info.CloudProjectInfos { + projects[i] = "(" + project.CloudProjectID + ")" + project.CloudProjectName + } + logs.Errorf("more than one project found: %s, rid: %s", strings.Join(projects, ","), cts.Kit.Rid) + return nil, fmt.Errorf("more than one project found: %s", strings.Join(projects, ",")) + } + result := info.CloudProjectInfos[0] + if req.DisableCheck { - return info, nil + return &result, nil } + if err = CheckDuplicateMainAccount(cts, a.client, enumor.Gcp, enumor.ResourceAccount, - info.CloudProjectID); err != nil { + result.CloudProjectID); err != nil { logs.Errorf("check whether main account duplicate fail, err: %v, rid: %s", err, cts.Kit.Rid) return nil, err } - return info, nil + return &result, nil } func (a *accountSvc) getAndCheckHuaWeiAccountInfo(cts *rest.Contexts) (*cloud.HuaWeiInfoBySecret, error) { diff --git a/cmd/hc-service/service/account/check.go b/cmd/hc-service/service/account/check.go index ac5cbc6b7c..4406acd7b7 100644 --- a/cmd/hc-service/service/account/check.go +++ b/cmd/hc-service/service/account/check.go @@ -21,6 +21,7 @@ package account import ( "hcm/pkg/adaptor/types" + "hcm/pkg/api/core/cloud" proto "hcm/pkg/api/hc-service/account" "hcm/pkg/criteria/errf" "hcm/pkg/rest" @@ -168,28 +169,34 @@ func (svc *service) GcpAccountCheck(cts *rest.Contexts) (interface{}, error) { return nil, err } - if infoBySecret.CloudProjectID != req.CloudProjectID { + var projectInfo cloud.GcpProjectInfo + for _, info := range infoBySecret.CloudProjectInfos { + if info.CloudProjectID == req.CloudProjectID { + projectInfo = info + break + } + } + if projectInfo.CloudProjectID != req.CloudProjectID { return nil, errf.New(errf.InvalidParameter, "CloudProjectID does not match the account to which the secret belongs") } - if infoBySecret.CloudProjectName != req.CloudProjectName { + if projectInfo.CloudProjectName != req.CloudProjectName { return nil, errf.New(errf.InvalidParameter, "CloudProjectName does not match the account to which the secret belongs") } - if infoBySecret.CloudServiceAccountID != req.CloudServiceAccountID { + if projectInfo.CloudServiceAccountID != req.CloudServiceAccountID { return nil, errf.New(errf.InvalidParameter, "CloudServiceAccountID does not match the account to which the secret belongs") } - if infoBySecret.CloudServiceAccountName != req.CloudServiceAccountName { + if projectInfo.CloudServiceAccountName != req.CloudServiceAccountName { return nil, errf.New(errf.InvalidParameter, "CloudServiceAccountName does not match the account to which the secret belongs") } - if infoBySecret.CloudServiceSecretID != req.CloudServiceSecretID { + if projectInfo.CloudServiceSecretID != req.CloudServiceSecretID { return nil, errf.New(errf.InvalidParameter, "CloudServiceSecretID does not match the account to which the secret belongs") } - - return nil, err + return nil, nil } // AzureAccountCheck ... @@ -218,15 +225,30 @@ func (svc *service) AzureAccountCheck(cts *rest.Contexts) (interface{}, error) { return nil, err } - if infoBySecret.CloudSubscriptionID != req.CloudSubscriptionID { + var curSubscription cloud.AzureSubscriptionInfo + for _, subscription := range infoBySecret.SubscriptionInfos { + if subscription.CloudSubscriptionID == req.CloudSubscriptionID { + curSubscription = subscription + break + } + } + if curSubscription.CloudSubscriptionID != req.CloudSubscriptionID { return nil, errf.New(errf.InvalidParameter, "CloudSubscriptionID does not match the account to which the secret belongs") } - if infoBySecret.CloudSubscriptionName != req.CloudSubscriptionName { + if curSubscription.CloudSubscriptionName != req.CloudSubscriptionName { return nil, errf.New(errf.InvalidParameter, "CloudSubscriptionName does not match the account to which the secret belongs") } - if infoBySecret.CloudApplicationName != req.CloudApplicationName { + + var curApplication cloud.AzureApplicationInfo + for _, application := range infoBySecret.ApplicationInfos { + if application.CloudApplicationID == req.CloudApplicationID { + curApplication = application + break + } + } + if curApplication.CloudApplicationName != req.CloudApplicationName { return nil, errf.New(errf.InvalidParameter, "CloudApplicationName does not match the account to which the secret belongs") } diff --git a/cmd/hc-service/service/account/get.go b/cmd/hc-service/service/account/get.go index 55063c54a7..b3906668cf 100644 --- a/cmd/hc-service/service/account/get.go +++ b/cmd/hc-service/service/account/get.go @@ -131,7 +131,6 @@ func (svc *service) GcpGetInfoBySecret(cts *rest.Contexts) (interface{}, error) } // 2. 云上信息获取 return client.GetAccountInfoBySecret(cts.Kit, req.CloudServiceSecretKey) - } // AzureGetInfoBySecret 根据秘钥信息去云上获取账号信息 diff --git a/docs/api-docs/web-server/docs/resource/account/get_account_by_secret.md b/docs/api-docs/web-server/docs/resource/account/get_account_by_secret.md new file mode 100644 index 0000000000..afad14fe0c --- /dev/null +++ b/docs/api-docs/web-server/docs/resource/account/get_account_by_secret.md @@ -0,0 +1,170 @@ +### 描述 + +- 该接口提供版本:v9.9.9+ +- 该接口所需权限:一级账号查看。 +- 该接口功能描述:通过秘钥获取账号信息。 + +### URL + +POST /api/v1/account/vendors/{vendor}/root_accounts/query_account_by_secret + +#### 路径参数说明 + +| 参数名称 | 参数类型 | 必选 | 描述 | +|--------|--------|----|-------------------------------| +| vendor | string | 是 | 云厂商(枚举值:aws、huawei、gcp、azure) | + +### 输入参数 + + +#### AWS + +| 参数名称 | 参数类型 | 必选 | 描述 | +|------------------|--------|----|-----------| +| cloud_secret_id | string | 是 | 云加密ID | +| cloud_secret_key | string | 是 | 云密钥 | + +#### Azure + +| 参数名称 | 参数类型 | 必选 | 描述 | +|-------------------------|--------|----|-----------| +| cloud_tenant_id | string | 是 | 云租户ID | +| cloud_application_id | string | 是 | 云应用ID | +| cloud_client_secret_key | string | 是 | 云客户端密钥 | + +#### GCP + +| 参数名称 | 参数类型 | 必选 | 描述 | +|--------------------------|--------|----|-----------| +| cloud_service_secret_key | string | 是 | 云服务密钥 | + +#### Huawei + +| 参数名称 | 参数类型 | 必选 | 描述 | +|------------------|--------|----|-----------| +| cloud_secret_id | string | 是 | 云加密ID | +| cloud_secret_key | string | 是 | 云密钥 | + +### 调用示例 + +#### TCloud + + +#### Aws + +```json +{ + "cloud_secret_id": "xxxx", + "cloud_secret_key": "xxxx" +} +``` + +#### Azure + +```json +{ + "cloud_tenant_id": "0000000", + "cloud_application_id": "xxxxxx", + "cloud_client_secret_key": "xxxxxx" +} +``` + +#### Gcp + +```json +{ + "cloud_service_secret_key": "{xxxx:xxx}" +} +``` + +#### HuaWei + +```json +{ + "cloud_secret_id": "xxxx", + "cloud_secret_key": "xxxx" +} +``` + +### 响应示例 + +```json +{ + "code": 0, + "message": "", + "data": { + "cloud_main_account_id": "00000001", + "cloud_sub_account_id": "xxxx" + } +} +``` + + + +### aws 响应参数说明 + +| 参数名称 | 参数类型 | 描述 | +|---------|--------|------| +| code | int32 | 状态码 | +| message | string | 请求信息 | +| data | object | 响应数据 | + +#### data[aws] + +| 参数名称 | 参数类型 | 描述 | +|--------------------|--------|---------| +| cloud_account_id | string | 云账户ID | +| cloud_iam_username | string | 云iam用户名 | + +### huawei 响应参数说明 + +| 参数名称 | 参数类型 | 描述 | +|---------|--------|------| +| code | int32 | 状态码 | +| message | string | 请求信息 | +| data | object | 响应数据 | + +#### data[huawei] + +| 参数名称 | 参数类型 | 描述 | +|------------------------|--------|----------| +| cloud_sub_account_id | string | 云子账户ID | +| cloud_sub_account_name | string | 云子账户名称 | +| cloud_iam_user_id | string | 云iam用户ID | +| cloud_iam_username | string | 云iam用户名 | + +### gcp 响应参数说明 + +| 参数名称 | 参数类型 | 描述 | +|---------|----------|---------------------------| +| code | int32 | 状态码 | +| message | string | 请求信息 | +| data | []object | 响应数据,一个对象数组,包含多个project信息 | + +#### data[gcp] + +| 参数名称 | 参数类型 | 描述 | +|----------------------------|--------|---------| +| email | string | 邮箱地址 | +| cloud_project_id | string | 云项目ID | +| cloud_project_name | string | 云项目名称 | +| cloud_service_account_id | string | 云服务账户ID | +| cloud_service_account_name | string | 云服务账户名称 | +| cloud_service_secret_id | string | 云服务秘钥ID | + +### azure 响应参数说明 + +| 参数名称 | 参数类型 | 描述 | +|---------|--------|------| +| code | int32 | 状态码 | +| message | string | 请求信息 | +| data | object | 响应数据 | + +#### data[azure] + +| 参数名称 | 参数类型 | 描述 | +|-------------------------|--------|-------| +| cloud_subscription_id | string | 云订阅ID | +| cloud_subscription_name | string | 云订阅名称 | +| cloud_application_name | string | 云应用名称 | + diff --git a/pkg/adaptor/azure/account.go b/pkg/adaptor/azure/account.go index c44b628895..aec14b454b 100644 --- a/pkg/adaptor/azure/account.go +++ b/pkg/adaptor/azure/account.go @@ -21,7 +21,6 @@ package azure import ( "fmt" - "strings" "hcm/pkg/adaptor/types/account" "hcm/pkg/api/core/cloud" @@ -96,7 +95,7 @@ func (az *Azure) GetAccountInfoBySecret(kt *kit.Kit) (*cloud.AzureInfoBySecret, } azInfo := new(cloud.AzureInfoBySecret) - // 1. 获取该账号可以访问的订阅,要求订阅数量刚好一个 + // 1. 获取该账号可以访问的订阅 // https://learn.microsoft.com/en-us/rest/api/resources/subscriptions/list pager := subClient.NewListPager(nil) if !pager.More() { @@ -106,21 +105,16 @@ func (az *Azure) GetAccountInfoBySecret(kt *kit.Kit) (*cloud.AzureInfoBySecret, if err != nil { return nil, err } - - subscriptions := subscriptionListResp.Value - if len(subscriptions) == 0 { + if len(subscriptionListResp.Value) == 0 { return nil, fmt.Errorf("no subscription found") } - if len(subscriptions) > 1 { - subs := make([]string, len(subscriptions)) - for i, sub := range subscriptions { - subs[i] = "(" + converter.PtrToVal(sub.SubscriptionID) + ")" + converter.PtrToVal(sub.DisplayName) - } - return nil, fmt.Errorf("more than one subscription found: " + strings.Join(subs, ",")) - } - azInfo.CloudSubscriptionName = converter.PtrToVal(subscriptions[0].DisplayName) - azInfo.CloudSubscriptionID = converter.PtrToVal(subscriptions[0].SubscriptionID) + for _, subscription := range subscriptionListResp.Value { + azInfo.SubscriptionInfos = append(azInfo.SubscriptionInfos, cloud.AzureSubscriptionInfo{ + CloudSubscriptionID: converter.PtrToVal(subscription.SubscriptionID), + CloudSubscriptionName: converter.PtrToVal(subscription.DisplayName), + }) + } // 2. 获取应用信息 https://learn.microsoft.com/en-us/graph/api/application-list resp, err := graphClient.Applications().Get(kt.Ctx, nil) @@ -130,16 +124,10 @@ func (az *Azure) GetAccountInfoBySecret(kt *kit.Kit) (*cloud.AzureInfoBySecret, } for _, one := range resp.GetValue() { - // 过滤id - if converter.PtrToVal(one.GetAppId()) == az.clientSet.credential.CloudApplicationID { - azInfo.CloudApplicationName = converter.PtrToVal(one.GetDisplayName()) - break - } - - } - // 没有拿到应用id的情况 - if len(azInfo.CloudApplicationName) == 0 { - return nil, fmt.Errorf("failed to get application name") + azInfo.ApplicationInfos = append(azInfo.ApplicationInfos, cloud.AzureApplicationInfo{ + CloudApplicationID: converter.PtrToVal(one.GetAppId()), + CloudApplicationName: converter.PtrToVal(one.GetDisplayName()), + }) } return azInfo, nil diff --git a/pkg/adaptor/gcp/account.go b/pkg/adaptor/gcp/account.go index 0a7f0628f0..3ea84afd82 100644 --- a/pkg/adaptor/gcp/account.go +++ b/pkg/adaptor/gcp/account.go @@ -176,41 +176,42 @@ func (g *Gcp) GetAccountInfoBySecret(kit *kit.Kit, cloudSecretKeyString string) logs.Errorf("search project failed, err: %v, rid: %s", err, kit.Rid) return nil, err } - // 2. 只能有一个对应的project,多project报错。 - if len(projectList.Projects) > 1 { - projects := make([]string, len(projectList.Projects)) - for i, project := range projectList.Projects { - projects[i] = "(" + project.ProjectId + ")" + project.DisplayName - } - - return nil, fmt.Errorf("more than one project found:" + strings.Join(projects, ",")) - } if len(projectList.Projects) == 0 { return nil, fmt.Errorf("not project avaiable, please check the permission of given screct") } - projectId := projectList.Projects[0].ProjectId + iamClient, err := g.clientSet.iamServiceClient(kit) if err != nil { return nil, err } - // 3. 根据秘钥信息获取服务账号信息 + // 2. 根据秘钥信息获取服务账号信息 // https://cloud.google.com/iam/docs/reference/rest/v1/projects.serviceAccounts/get sk, err := account.DecodeGcpSecretKey(cloudSecretKeyString) - serviceAccount, err := iamClient.Projects.ServiceAccounts.Get( - fmt.Sprintf("projects/%s/serviceAccounts/%s", projectId, sk.ClientEmail), - ).Do() if err != nil { return nil, err } + projectInfos := make([]cloud.GcpProjectInfo, 0) + for _, project := range projectList.Projects { + serviceAccount, err := iamClient.Projects.ServiceAccounts.Get( + fmt.Sprintf("projects/%s/serviceAccounts/%s", project.ProjectId, sk.ClientEmail), + ).Do() + if err != nil { + return nil, err + } + projectInfos = append(projectInfos, cloud.GcpProjectInfo{ + Email: serviceAccount.Email, + CloudProjectID: project.ProjectId, + CloudProjectName: project.DisplayName, + CloudServiceAccountID: serviceAccount.UniqueId, + CloudServiceAccountName: serviceAccount.DisplayName, + CloudServiceSecretID: sk.PrivateKeyID, + }) + } + accountInfo := &cloud.GcpInfoBySecret{ - Email: serviceAccount.Email, - CloudProjectID: projectList.Projects[0].ProjectId, - CloudProjectName: projectList.Projects[0].DisplayName, - CloudServiceAccountID: serviceAccount.UniqueId, - CloudServiceAccountName: serviceAccount.DisplayName, - CloudServiceSecretID: sk.PrivateKeyID, + CloudProjectInfos: projectInfos, } return accountInfo, nil } diff --git a/pkg/api/account-server/account-set/root_account.go b/pkg/api/account-server/account-set/root_account.go index 6105743b08..37876bb74f 100644 --- a/pkg/api/account-server/account-set/root_account.go +++ b/pkg/api/account-server/account-set/root_account.go @@ -23,6 +23,7 @@ package accountset import ( "encoding/json" + "hcm/pkg/api/core/cloud" "hcm/pkg/criteria/enumor" "hcm/pkg/criteria/validator" ) @@ -174,3 +175,55 @@ func (req *KaopuRootAccountExtensionUpdateReq) Validate() error { return nil } + +// GcpAccountInfoBySecretReq ... +type GcpAccountInfoBySecretReq struct { + *cloud.GcpSecret `json:",inline" validate:"required"` +} + +// Validate ... +func (req *GcpAccountInfoBySecretReq) Validate() error { + if err := req.GcpSecret.Validate(); err != nil { + return err + } + return validator.Validate.Struct(req) +} + +// AwsAccountInfoBySecretReq ... +type AwsAccountInfoBySecretReq struct { + *cloud.AwsSecret `json:",inline" validate:"required"` +} + +// Validate ... +func (req *AwsAccountInfoBySecretReq) Validate() error { + if err := req.AwsSecret.Validate(); err != nil { + return err + } + return validator.Validate.Struct(req) +} + +// HuaWeiAccountInfoBySecretReq ... +type HuaWeiAccountInfoBySecretReq struct { + *cloud.HuaWeiSecret `json:",inline" validate:"required"` +} + +// Validate ... +func (req *HuaWeiAccountInfoBySecretReq) Validate() error { + if err := req.HuaWeiSecret.Validate(); err != nil { + return err + } + return validator.Validate.Struct(req) +} + +// AzureAccountInfoBySecretReq ... +type AzureAccountInfoBySecretReq struct { + *cloud.AzureSecret `json:",inline" validate:"required"` +} + +// Validate ... +func (req *AzureAccountInfoBySecretReq) Validate() error { + if err := req.AzureSecret.Validate(); err != nil { + return err + } + return validator.Validate.Struct(req) +} diff --git a/pkg/api/cloud-server/account/get.go b/pkg/api/cloud-server/account/get.go index cc6c76d505..21662c4245 100644 --- a/pkg/api/cloud-server/account/get.go +++ b/pkg/api/cloud-server/account/get.go @@ -88,6 +88,13 @@ func (req *AzureAccountInfoBySecretReq) Validate() error { return validator.Validate.Struct(req) } +// AzureAccountInfoBySecretResp ... +type AzureAccountInfoBySecretResp struct { + CloudSubscriptionID string `json:"cloud_subscription_id"` + CloudSubscriptionName string `json:"cloud_subscription_name"` + CloudApplicationName string `json:"cloud_application_name"` +} + // GcpAccountInfoBySecretReq ... type GcpAccountInfoBySecretReq struct { DisableCheck bool `json:"disable_check" validate:"omitempty"` diff --git a/pkg/api/core/cloud/account_by_secret.go b/pkg/api/core/cloud/account_by_secret.go index 81fa919222..0e8a7015fc 100644 --- a/pkg/api/core/cloud/account_by_secret.go +++ b/pkg/api/core/cloud/account_by_secret.go @@ -19,7 +19,9 @@ package cloud -import "hcm/pkg/criteria/validator" +import ( + "hcm/pkg/criteria/validator" +) // AccountInfoBySecret 根据秘钥获取的账号字段 type AccountInfoBySecret interface { @@ -48,6 +50,11 @@ type HuaWeiInfoBySecret struct { // GcpInfoBySecret GCP 根据秘钥获取的字段 type GcpInfoBySecret struct { + CloudProjectInfos []GcpProjectInfo `json:"cloud_project_infos"` +} + +// GcpProjectInfo GCP 单个project的字段信息 +type GcpProjectInfo struct { Email string `json:"email"` CloudProjectID string `json:"cloud_project_id"` CloudProjectName string `json:"cloud_project_name"` @@ -58,9 +65,20 @@ type GcpInfoBySecret struct { // AzureInfoBySecret Azure 根据秘钥获取的字段 type AzureInfoBySecret struct { + SubscriptionInfos []AzureSubscriptionInfo `json:"cloud_subscription_infos"` + ApplicationInfos []AzureApplicationInfo `json:"cloud_application_infos"` +} + +// AzureApplicationInfo Azure 单个应用实例的字段信息 +type AzureApplicationInfo struct { + CloudApplicationID string `json:"cloud_application_id"` + CloudApplicationName string `json:"cloud_application_name"` +} + +// AzureSubscriptionInfo Azure 单个订阅的字段信息 +type AzureSubscriptionInfo struct { CloudSubscriptionID string `json:"cloud_subscription_id"` CloudSubscriptionName string `json:"cloud_subscription_name"` - CloudApplicationName string `json:"cloud_application_name"` } // AccountSecret 账号所需秘钥 @@ -75,6 +93,7 @@ type TCloudSecret struct { CloudSecretKey string `json:"cloud_secret_key" validate:"required"` } +// Validate ... func (sk TCloudSecret) Validate() error { return validator.Validate.Struct(sk) } @@ -85,6 +104,7 @@ type AwsSecret struct { CloudSecretKey string `json:"cloud_secret_key" validate:"required"` } +// Validate ... func (sk AwsSecret) Validate() error { return validator.Validate.Struct(sk) } @@ -95,6 +115,7 @@ type HuaWeiSecret struct { CloudSecretKey string `json:"cloud_secret_key" validate:"required"` } +// Validate ... func (sk HuaWeiSecret) Validate() error { return validator.Validate.Struct(sk) } @@ -115,6 +136,7 @@ func (g *GcpCredential) Validate() error { return validator.Validate.Struct(g) } +// Validate ... func (sk GcpSecret) Validate() error { return validator.Validate.Struct(sk) } @@ -126,6 +148,7 @@ type AzureSecret struct { CloudClientSecretKey string `json:"cloud_client_secret_key" validate:"required"` } +// Validate ... func (sk AzureSecret) Validate() error { return validator.Validate.Struct(sk) } @@ -138,6 +161,7 @@ type AzureAuthSecret struct { CloudClientSecretKey string `json:"cloud_client_secret_key" validate:"required"` } +// Validate ... func (sk AzureAuthSecret) Validate() error { return validator.Validate.Struct(sk) }