mirror of
https://github.com/certimate-go/certimate.git
synced 2026-10-16 07:05:59 +08:00
feat(provider): new purge provider: huaweicloud waf
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
package purgers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/certimate-go/certimate/internal/domain"
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
pgrimpl "github.com/certimate-go/certimate/pkg/core/purger/providers/huaweicloud-waf"
|
||||
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Registries.MustRegister(domain.PurgeProviderTypeHuaweiCloudWAF, func(options *ProviderFactoryOptions) (core.Purger, error) {
|
||||
credentials := domain.AccessConfigForHuaweiCloud{}
|
||||
if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil {
|
||||
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
|
||||
}
|
||||
|
||||
provider, err := pgrimpl.NewPurger(&pgrimpl.PurgerConfig{
|
||||
AccessKeyId: credentials.AccessKeyId,
|
||||
SecretAccessKey: credentials.SecretAccessKey,
|
||||
EnterpriseProjectId: credentials.EnterpriseProjectId,
|
||||
Region: xmaps.GetString(options.ProviderExtendedConfig, "region"),
|
||||
})
|
||||
return provider, err
|
||||
})
|
||||
}
|
||||
@@ -497,6 +497,7 @@ const (
|
||||
PurgeProviderTypeAliyunESA = PurgeProviderType(AccessProviderTypeAliyun + "-esa")
|
||||
PurgeProviderTypeHuaweiCloudELB = PurgeProviderType(AccessProviderTypeHuaweiCloud + "-elb")
|
||||
PurgeProviderTypeHuaweiCloudSCM = PurgeProviderType(AccessProviderTypeHuaweiCloud + "-scm")
|
||||
PurgeProviderTypeHuaweiCloudWAF = PurgeProviderType(AccessProviderTypeHuaweiCloud + "-waf")
|
||||
PurgeProviderTypeTencentCloudGAAP = PurgeProviderType(AccessProviderTypeTencentCloud + "-gaap")
|
||||
PurgeProviderTypeTencentCloudSSL = PurgeProviderType(AccessProviderTypeTencentCloud + "-ssl")
|
||||
PurgeProviderTypeTencentCloudTSE = PurgeProviderType(AccessProviderTypeTencentCloud + "-tse")
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
//go:build tester
|
||||
|
||||
package huaweicloudwaf_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
tester "github.com/certimate-go/certimate/pkg/core/purger/providers-tester"
|
||||
impl "github.com/certimate-go/certimate/pkg/core/purger/providers/huaweicloud-waf"
|
||||
)
|
||||
|
||||
var (
|
||||
fp = tester.InitArgs("HUAWEICLOUDWAF_")
|
||||
fAccessKeyId string
|
||||
fSecretAccessKey string
|
||||
fRegion string
|
||||
)
|
||||
|
||||
func init() {
|
||||
fp.DefineString(&fAccessKeyId, "ACCESSKEYID")
|
||||
fp.DefineString(&fSecretAccessKey, "SECRETACCESSKEY")
|
||||
fp.DefineString(&fRegion, "REGION")
|
||||
}
|
||||
|
||||
/*
|
||||
Shell command to run this test:
|
||||
|
||||
go test -tags=tester -v ./huaweicloud_waf_test.go -args \
|
||||
--HUAWEICLOUDWAF_ACCESSKEYID="your-access-key-id" \
|
||||
--HUAWEICLOUDWAF_SECRETACCESSKEY="your-access-key-secret" \
|
||||
--HUAWEICLOUDWAF_REGION="cn-north-4"
|
||||
*/
|
||||
func TestProvider(t *testing.T) {
|
||||
fp.Parse()
|
||||
|
||||
t.Run("Purge", func(t *testing.T) {
|
||||
provider, err := impl.NewPurger(&impl.PurgerConfig{
|
||||
AccessKeyId: fAccessKeyId,
|
||||
SecretAccessKey: fSecretAccessKey,
|
||||
Region: fRegion,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
tester.Purge(t, provider, tester.PurgeInput{})
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
package huaweicloudwaf
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/basic"
|
||||
"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/global"
|
||||
hwwafmodel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/waf/v1/model"
|
||||
hwwafregion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/waf/v1/region"
|
||||
"github.com/samber/lo"
|
||||
|
||||
hwwaf "github.com/certimate-go/certimate/pkg/sdk3rd-trimmed/github.com/huaweicloud/huaweicloud-sdk-go-v3/services/waf/v1"
|
||||
|
||||
"github.com/certimate-go/certimate/pkg/core"
|
||||
xloop "github.com/certimate-go/certimate/pkg/utils/loop"
|
||||
xhuaweicloud "github.com/certimate-go/certimate/pkg/utils/third-party/huaweicloud"
|
||||
xwait "github.com/certimate-go/certimate/pkg/utils/wait"
|
||||
)
|
||||
|
||||
type (
|
||||
Provider = core.Purger
|
||||
PurgeResult = core.PurgerPurgeResult
|
||||
)
|
||||
|
||||
type PurgerConfig struct {
|
||||
// 华为云 AccessKeyId。
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
// 华为云 SecretAccessKey。
|
||||
SecretAccessKey string `json:"secretAccessKey"`
|
||||
// 华为云企业项目 ID。
|
||||
EnterpriseProjectId string `json:"enterpriseProjectId,omitempty"`
|
||||
// 华为云区域。
|
||||
Region string `json:"region"`
|
||||
}
|
||||
|
||||
type Purger struct {
|
||||
config *PurgerConfig
|
||||
logger *slog.Logger
|
||||
sdkClient *hwwaf.WafClient
|
||||
}
|
||||
|
||||
var _ Provider = (*Purger)(nil)
|
||||
|
||||
func NewPurger(config *PurgerConfig) (*Purger, error) {
|
||||
if config == nil {
|
||||
return nil, fmt.Errorf("the configuration of the purger provider is nil")
|
||||
}
|
||||
|
||||
client, err := createSDKClient(config.AccessKeyId, config.SecretAccessKey, config.Region)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create client: %w", err)
|
||||
}
|
||||
|
||||
return &Purger{
|
||||
config: config,
|
||||
logger: slog.Default(),
|
||||
sdkClient: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Purger) SetLogger(logger *slog.Logger) {
|
||||
if logger == nil {
|
||||
p.logger = slog.New(slog.DiscardHandler)
|
||||
} else {
|
||||
p.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Purger) Purge(ctx context.Context, expiry time.Duration) (*PurgeResult, error) {
|
||||
purgingCertIds := make([]string, 0)
|
||||
|
||||
// 查询证书列表
|
||||
// REF: https://support.huaweicloud.com/api-waf/ListCertificates.html
|
||||
listCertificatesPage := 1
|
||||
listCertificatesPageSize := 100
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
listCertificatesReq := &hwwafmodel.ListCertificatesRequest{
|
||||
EnterpriseProjectId: lo.EmptyableToPtr(p.config.EnterpriseProjectId),
|
||||
ExpStatus: lo.ToPtr(hwwafmodel.GetListCertificatesRequestExpStatusEnum().E_1),
|
||||
Page: lo.ToPtr(int32(listCertificatesPage)),
|
||||
Pagesize: lo.ToPtr(int32(listCertificatesPageSize)),
|
||||
}
|
||||
listCertificatesResp, err := p.sdkClient.ListCertificates(listCertificatesReq)
|
||||
p.logger.Debug("sdk request 'waf.ListCertificates'", slog.Any("request", listCertificatesReq), slog.Any("response", listCertificatesResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'waf.ListCertificates': %w", err)
|
||||
}
|
||||
|
||||
for _, certItem := range *listCertificatesResp.Items {
|
||||
certNotAfter := time.UnixMilli(lo.FromPtr(certItem.ExpireTime))
|
||||
if time.Since(certNotAfter) >= expiry {
|
||||
purgingCertIds = append(purgingCertIds, certItem.Id)
|
||||
}
|
||||
}
|
||||
|
||||
if len(*listCertificatesResp.Items) < listCertificatesPageSize {
|
||||
break
|
||||
}
|
||||
|
||||
listCertificatesPage++
|
||||
}
|
||||
|
||||
// 删除证书
|
||||
// REF: https://support.huaweicloud.com/api-waf/DeleteCertificate.html
|
||||
purgedCount := 0
|
||||
if len(purgingCertIds) > 0 {
|
||||
p.logger.Info("found certificates to purge", slog.Any("certIds", purgingCertIds))
|
||||
|
||||
if err := xloop.ForRangeAllWithContext(ctx, purgingCertIds, func(ctx context.Context, certId string, i int) error {
|
||||
if i > 0 {
|
||||
if err := xwait.DelayWithContext(ctx, 1*time.Second); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
deleteCertificateReq := &hwwafmodel.DeleteCertificateRequest{
|
||||
EnterpriseProjectId: lo.EmptyableToPtr(p.config.EnterpriseProjectId),
|
||||
CertificateId: certId,
|
||||
}
|
||||
deleteCertificateResp, err := p.sdkClient.DeleteCertificate(deleteCertificateReq)
|
||||
p.logger.Debug("sdk request 'waf.DeleteCertificate'", slog.Any("request", deleteCertificateReq), slog.Any("response", deleteCertificateResp))
|
||||
if err != nil {
|
||||
if deleteCertificateResp != nil && deleteCertificateResp.HttpStatusCode == 404 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("failed to execute sdk request 'waf.DeleteCertificate': %w", err)
|
||||
}
|
||||
|
||||
purgedCount++
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &PurgeResult{
|
||||
Count: purgedCount,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func createSDKClient(accessKeyId, secretAccessKey, region string) (*hwwaf.WafClient, error) {
|
||||
projectId, err := getSDKProjectId(accessKeyId, secretAccessKey, region)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
auth, err := basic.NewCredentialsBuilder().
|
||||
WithAk(accessKeyId).
|
||||
WithSk(secretAccessKey).
|
||||
WithProjectId(projectId).
|
||||
SafeBuild()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hcRegion, err := hwwafregion.SafeValueOf(region)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hcClient, err := hwwaf.WafClientBuilder().
|
||||
WithRegion(hcRegion).
|
||||
WithCredential(auth).
|
||||
SafeBuild()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := hwwaf.NewWafClient(hcClient)
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func getSDKProjectId(accessKeyId, secretAccessKey, region string) (string, error) {
|
||||
auth, err := global.NewCredentialsBuilder().
|
||||
WithAk(accessKeyId).
|
||||
WithSk(secretAccessKey).
|
||||
SafeBuild()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return xhuaweicloud.GetKeystoneProjectIDWithRegion(auth, region)
|
||||
}
|
||||
+10
@@ -28,6 +28,16 @@ func (c *WafClient) CreateCertificate(request *model.CreateCertificateRequest) (
|
||||
}
|
||||
}
|
||||
|
||||
func (c *WafClient) DeleteCertificate(request *model.DeleteCertificateRequest) (*model.DeleteCertificateResponse, error) {
|
||||
requestDef := GenReqDefForDeleteCertificate()
|
||||
|
||||
if resp, err := c.HcClient.Sync(request, requestDef); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return resp.(*model.DeleteCertificateResponse), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *WafClient) ListCertificates(request *model.ListCertificatesRequest) (*model.ListCertificatesResponse, error) {
|
||||
requestDef := GenReqDefForListCertificates()
|
||||
|
||||
|
||||
+4
@@ -9,6 +9,10 @@ func GenReqDefForCreateCertificate() *def.HttpRequestDef {
|
||||
return waf.GenReqDefForCreateCertificate()
|
||||
}
|
||||
|
||||
func GenReqDefForDeleteCertificate() *def.HttpRequestDef {
|
||||
return waf.GenReqDefForDeleteCertificate()
|
||||
}
|
||||
|
||||
func GenReqDefForListCertificates() *def.HttpRequestDef {
|
||||
return waf.GenReqDefForListCertificates()
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import BizPurgeNodeConfigFieldsProviderAliyunCAS from "./BizPurgeNodeConfigField
|
||||
import BizPurgeNodeConfigFieldsProviderAliyunCLB from "./BizPurgeNodeConfigFieldsProviderAliyunCLB";
|
||||
import BizPurgeNodeConfigFieldsProviderAliyunESA from "./BizPurgeNodeConfigFieldsProviderAliyunESA";
|
||||
import BizPurgeNodeConfigFieldsProviderHuaweiCloudELB from "./BizPurgeNodeConfigFieldsProviderHuaweiCloudELB";
|
||||
import BizPurgeNodeConfigFieldsProviderHuaweiCloudWAF from "./BizPurgeNodeConfigFieldsProviderHuaweiCloudWAF";
|
||||
import BizPurgeNodeConfigFieldsProviderTencentCloudGAAP from "./BizPurgeNodeConfigFieldsProviderTencentCloudGAAP";
|
||||
import BizPurgeNodeConfigFieldsProviderTencentCloudSSL from "./BizPurgeNodeConfigFieldsProviderTencentCloudSSL";
|
||||
import BizPurgeNodeConfigFieldsProviderTencentCloudTSE from "./BizPurgeNodeConfigFieldsProviderTencentCloudTSE";
|
||||
@@ -20,6 +21,7 @@ const providerComponentMap: Partial<Record<PurgeProviderType, React.ComponentTyp
|
||||
[PURGE_PROVIDERS.ALIYUN_CLB]: BizPurgeNodeConfigFieldsProviderAliyunCLB,
|
||||
[PURGE_PROVIDERS.ALIYUN_ESA]: BizPurgeNodeConfigFieldsProviderAliyunESA,
|
||||
[PURGE_PROVIDERS.HUAWEICLOUD_ELB]: BizPurgeNodeConfigFieldsProviderHuaweiCloudELB,
|
||||
[PURGE_PROVIDERS.HUAWEICLOUD_WAF]: BizPurgeNodeConfigFieldsProviderHuaweiCloudWAF,
|
||||
[PURGE_PROVIDERS.TENCENTCLOUD_GAAP]: BizPurgeNodeConfigFieldsProviderTencentCloudGAAP,
|
||||
[PURGE_PROVIDERS.TENCENTCLOUD_SSL]: BizPurgeNodeConfigFieldsProviderTencentCloudSSL,
|
||||
[PURGE_PROVIDERS.TENCENTCLOUD_TSE]: BizPurgeNodeConfigFieldsProviderTencentCloudTSE,
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
import { getI18n, useTranslation } from "react-i18next";
|
||||
import { Form, Input } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import { useFormNestedFieldsContext } from "./_context";
|
||||
|
||||
const BizPurgeNodeConfigFieldsProviderHuaweiCloudWAF = () => {
|
||||
const { i18n, t } = useTranslation();
|
||||
|
||||
const { parentNamePath } = useFormNestedFieldsContext();
|
||||
const formSchema = z.object({
|
||||
[parentNamePath]: getSchema({ i18n }),
|
||||
});
|
||||
const formRule = createSchemaFieldRule(formSchema);
|
||||
const initialValues = getInitialValues();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
name={[parentNamePath, "region"]}
|
||||
initialValue={initialValues.region}
|
||||
label={t("workflow_node.purge.form.huaweicloud_waf_region.label")}
|
||||
rules={[formRule]}
|
||||
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.purge.form.huaweicloud_waf_region.tooltip") }}></span>}
|
||||
>
|
||||
<Input placeholder={t("workflow_node.purge.form.huaweicloud_waf_region.placeholder")} />
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
|
||||
return {
|
||||
region: "",
|
||||
};
|
||||
};
|
||||
|
||||
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
|
||||
const { t: _ } = i18n;
|
||||
|
||||
return z.object({
|
||||
region: z.string().nonempty(),
|
||||
});
|
||||
};
|
||||
|
||||
const _default = Object.assign(BizPurgeNodeConfigFieldsProviderHuaweiCloudWAF, {
|
||||
getInitialValues,
|
||||
getSchema,
|
||||
});
|
||||
|
||||
export default _default;
|
||||
@@ -991,6 +991,7 @@ export const PURGE_PROVIDERS = Object.freeze(
|
||||
ALIYUN_ESA: `${ACCESS_PROVIDERS.ALIYUN}-esa`,
|
||||
HUAWEICLOUD_ELB: `${ACCESS_PROVIDERS.HUAWEICLOUD}-elb`,
|
||||
HUAWEICLOUD_SCM: `${ACCESS_PROVIDERS.HUAWEICLOUD}-scm`,
|
||||
HUAWEICLOUD_WAF: `${ACCESS_PROVIDERS.HUAWEICLOUD}-waf`,
|
||||
TENCENTCLOUD_GAAP: `${ACCESS_PROVIDERS.TENCENTCLOUD}-gaap`,
|
||||
TENCENTCLOUD_SSL: `${ACCESS_PROVIDERS.TENCENTCLOUD}-ssl`,
|
||||
TENCENTCLOUD_TSE: `${ACCESS_PROVIDERS.TENCENTCLOUD}-tse`,
|
||||
@@ -1017,6 +1018,7 @@ export const purgeProvidersMap: Map<PurgeProvider["type"] | string, PurgeProvide
|
||||
[PURGE_PROVIDERS.TENCENTCLOUD_TSE, "provider.tencentcloud_tse"],
|
||||
[PURGE_PROVIDERS.HUAWEICLOUD_SCM, "provider.huaweicloud_scm"],
|
||||
[PURGE_PROVIDERS.HUAWEICLOUD_ELB, "provider.huaweicloud_elb"],
|
||||
[PURGE_PROVIDERS.HUAWEICLOUD_WAF, "provider.huaweicloud_waf"],
|
||||
[PURGE_PROVIDERS.UCLOUD_USSL, "provider.ucloud_ussl"],
|
||||
] satisfies Array<[PurgeProviderType, string, "builtin"] | [PurgeProviderType, string]>
|
||||
).map(([type, name]) => [
|
||||
|
||||
@@ -3057,6 +3057,11 @@
|
||||
"placeholder": "Please enter Huawei Cloud ELB region (e.g. cn-north-1)",
|
||||
"tooltip": "For more information, see <a href=\"https://console-intl.huaweicloud.com/apiexplorer/#/endpoint/elb?locale=en-us\" target=\"_blank\">https://console-intl.huaweicloud.com/apiexplorer/#/endpoint/elb</a>"
|
||||
},
|
||||
"huaweicloud_waf_region": {
|
||||
"label": "Huawei Cloud region",
|
||||
"placeholder": "Please enter Huawei Cloud WAF region (e.g. cn-north-1)",
|
||||
"tooltip": "For more information, see <a href=\"https://console-intl.huaweicloud.com/apiexplorer/#/endpoint/waf?locale=en-us\" target=\"_blank\">https://console-intl.huaweicloud.com/apiexplorer/#/endpoint/waf</a>"
|
||||
},
|
||||
"tencentcloud_gaap_endpoint": {
|
||||
"label": "Tencent Cloud API endpoint (Optional)",
|
||||
"placeholder": "Please enter Tencent Cloud GAAP API endpoint (e.g. gaap.intl.tencentcloudapi.com)",
|
||||
|
||||
@@ -3056,6 +3056,11 @@
|
||||
"placeholder": "请输入华为云 ELB 服务区域(例如:cn-north-1)",
|
||||
"tooltip": "这是什么?请参阅 <a href=\"https://console.huaweicloud.com/apiexplorer/#/endpoint/elb\" target=\"_blank\">https://console.huaweicloud.com/apiexplorer/#/endpoint/elb</a>"
|
||||
},
|
||||
"huaweicloud_waf_region": {
|
||||
"label": "华为云服务区域",
|
||||
"placeholder": "请输入华为云 WAF 服务区域(例如:cn-north-1)",
|
||||
"tooltip": "这是什么?请参阅 <a href=\"https://console.huaweicloud.com/apiexplorer/#/endpoint/waf\" target=\"_blank\">https://console.huaweicloud.com/apiexplorer/#/endpoint/waf</a>"
|
||||
},
|
||||
"tencentcloud_gaap_endpoint": {
|
||||
"label": "腾讯云接口端点(可选)",
|
||||
"placeholder": "请输入腾讯云 GAAP 接口端点(例如:gaap.tencentcloudapi.com)",
|
||||
|
||||
Reference in New Issue
Block a user