From 7edf4488f34c9fd08845c8f87170361a8c54302a Mon Sep 17 00:00:00 2001 From: Rong Zhou Date: Thu, 5 Sep 2024 07:33:32 +0000 Subject: [PATCH] add audit package --- Makefile | 1 + api-specs | 2 +- audit/apis/api_query_log.go | 122 ++++++++++++++++++ audit/apis/apis.go | 27 ++++ audit/apis/query_log/api.go | 241 ++++++++++++++++++++++++++++++++++++ audit/doc.go | 5 + 6 files changed, 397 insertions(+), 1 deletion(-) create mode 100644 audit/apis/api_query_log.go create mode 100644 audit/apis/apis.go create mode 100644 audit/apis/query_log/api.go create mode 100644 audit/doc.go diff --git a/Makefile b/Makefile index a0e3aba4..9c6bdab1 100644 --- a/Makefile +++ b/Makefile @@ -14,3 +14,4 @@ generate: go generate ./storagev2/ go generate ./iam/ go generate ./media/ + go generate ./audit/ diff --git a/api-specs b/api-specs index fdbd3ec7..df0804fb 160000 --- a/api-specs +++ b/api-specs @@ -1 +1 @@ -Subproject commit fdbd3ec75dd2024454a3ad0fef0bdf1a8a032d85 +Subproject commit df0804fb31d2629fb311c9182ea47deb39f4d555 diff --git a/audit/apis/api_query_log.go b/audit/apis/api_query_log.go new file mode 100644 index 00000000..1e2b328d --- /dev/null +++ b/audit/apis/api_query_log.go @@ -0,0 +1,122 @@ +// THIS FILE IS GENERATED BY api-generator, DO NOT EDIT DIRECTLY! + +package apis + +import ( + "context" + querylog "github.com/qiniu/go-sdk/v7/audit/apis/query_log" + auth "github.com/qiniu/go-sdk/v7/auth" + uplog "github.com/qiniu/go-sdk/v7/internal/uplog" + errors "github.com/qiniu/go-sdk/v7/storagev2/errors" + httpclient "github.com/qiniu/go-sdk/v7/storagev2/http_client" + region "github.com/qiniu/go-sdk/v7/storagev2/region" + uptoken "github.com/qiniu/go-sdk/v7/storagev2/uptoken" + "net/url" + "strings" + "time" +) + +type innerQueryLogRequest querylog.Request + +func (query *innerQueryLogRequest) buildQuery() (url.Values, error) { + allQuery := make(url.Values) + if query.StartTime != "" { + allQuery.Set("start_time", query.StartTime) + } else { + return nil, errors.MissingRequiredFieldError{Name: "StartTime"} + } + if query.EndTime != "" { + allQuery.Set("end_time", query.EndTime) + } else { + return nil, errors.MissingRequiredFieldError{Name: "EndTime"} + } + if query.ServiceName != "" { + allQuery.Set("service_name", query.ServiceName) + } + if len(query.EventNames) > 0 { + for _, value := range query.EventNames { + allQuery.Add("event_names", value) + } + } + if query.PrincipalId != "" { + allQuery.Set("principal_id", query.PrincipalId) + } + if query.AccessKeyId != "" { + allQuery.Set("access_key_id", query.AccessKeyId) + } + if query.Limit != "" { + allQuery.Set("limit", query.Limit) + } + if query.NextMark != "" { + allQuery.Set("next_mark", query.NextMark) + } + return allQuery, nil +} + +type QueryLogRequest = querylog.Request +type QueryLogResponse = querylog.Response + +// 审计日志查询 +func (audit *Audit) QueryLog(ctx context.Context, request *QueryLogRequest, options *Options) (*QueryLogResponse, error) { + if options == nil { + options = &Options{} + } + innerRequest := (*innerQueryLogRequest)(request) + serviceNames := []region.ServiceName{region.ServiceApi} + if innerRequest.Credentials == nil && audit.client.GetCredentials() == nil { + return nil, errors.MissingRequiredFieldError{Name: "Credentials"} + } + pathSegments := make([]string, 0, 2) + pathSegments = append(pathSegments, "audit", "log-query") + path := "/" + strings.Join(pathSegments, "/") + var rawQuery string + if query, err := innerRequest.buildQuery(); err != nil { + return nil, err + } else { + rawQuery += query.Encode() + } + uplogInterceptor, err := uplog.NewRequestUplog("queryLog", "", "", func() (string, error) { + credentials := innerRequest.Credentials + if credentials == nil { + credentials = audit.client.GetCredentials() + } + putPolicy, err := uptoken.NewPutPolicy("", time.Now().Add(time.Hour)) + if err != nil { + return "", err + } + return uptoken.NewSigner(putPolicy, credentials).GetUpToken(ctx) + }) + if err != nil { + return nil, err + } + req := httpclient.Request{Method: "GET", ServiceNames: serviceNames, Path: path, RawQuery: rawQuery, Endpoints: options.OverwrittenEndpoints, Region: options.OverwrittenRegion, Interceptors: []httpclient.Interceptor{uplogInterceptor}, AuthType: auth.TokenQiniu, Credentials: innerRequest.Credentials, BufferResponse: true, OnRequestProgress: options.OnRequestProgress} + if options.OverwrittenEndpoints == nil && options.OverwrittenRegion == nil && audit.client.GetRegions() == nil { + bucketHosts := httpclient.DefaultBucketHosts() + + req.Region = audit.client.GetAllRegions() + if req.Region == nil { + if options.OverwrittenBucketHosts != nil { + if bucketHosts, err = options.OverwrittenBucketHosts.GetEndpoints(ctx); err != nil { + return nil, err + } + } + allRegionsOptions := region.AllRegionsProviderOptions{UseInsecureProtocol: audit.client.UseInsecureProtocol(), HostFreezeDuration: audit.client.GetHostFreezeDuration(), Resolver: audit.client.GetResolver(), Chooser: audit.client.GetChooser(), BeforeSign: audit.client.GetBeforeSignCallback(), AfterSign: audit.client.GetAfterSignCallback(), SignError: audit.client.GetSignErrorCallback(), BeforeResolve: audit.client.GetBeforeResolveCallback(), AfterResolve: audit.client.GetAfterResolveCallback(), ResolveError: audit.client.GetResolveErrorCallback(), BeforeBackoff: audit.client.GetBeforeBackoffCallback(), AfterBackoff: audit.client.GetAfterBackoffCallback(), BeforeRequest: audit.client.GetBeforeRequestCallback(), AfterResponse: audit.client.GetAfterResponseCallback()} + if hostRetryConfig := audit.client.GetHostRetryConfig(); hostRetryConfig != nil { + allRegionsOptions.RetryMax = hostRetryConfig.RetryMax + allRegionsOptions.Backoff = hostRetryConfig.Backoff + } + credentials := innerRequest.Credentials + if credentials == nil { + credentials = audit.client.GetCredentials() + } + if req.Region, err = region.NewAllRegionsProvider(credentials, bucketHosts, &allRegionsOptions); err != nil { + return nil, err + } + } + } + var respBody QueryLogResponse + if err := audit.client.DoAndAcceptJSON(ctx, &req, &respBody); err != nil { + return nil, err + } + return &respBody, nil +} diff --git a/audit/apis/apis.go b/audit/apis/apis.go new file mode 100644 index 00000000..56634edb --- /dev/null +++ b/audit/apis/apis.go @@ -0,0 +1,27 @@ +// THIS FILE IS GENERATED BY api-generator, DO NOT EDIT DIRECTLY! + +package apis + +import ( + httpclient "github.com/qiniu/go-sdk/v7/storagev2/http_client" + region "github.com/qiniu/go-sdk/v7/storagev2/region" +) + +// API 客户端 +type Audit struct { + client *httpclient.Client +} + +// 创建 API 客户端 +func NewAudit(options *httpclient.Options) *Audit { + return &Audit{client: httpclient.NewClient(options)} +} + +// API 客户端选项 +type Options struct { + OverwrittenBucketHosts region.EndpointsProvider + OverwrittenBucketName string + OverwrittenEndpoints region.EndpointsProvider + OverwrittenRegion region.RegionsProvider + OnRequestProgress func(uint64, uint64) +} diff --git a/audit/apis/query_log/api.go b/audit/apis/query_log/api.go new file mode 100644 index 00000000..215ca2b0 --- /dev/null +++ b/audit/apis/query_log/api.go @@ -0,0 +1,241 @@ +// THIS FILE IS GENERATED BY api-generator, DO NOT EDIT DIRECTLY! + +// 审计日志查询 +package query_log + +import ( + "encoding/json" + credentials "github.com/qiniu/go-sdk/v7/storagev2/credentials" + errors "github.com/qiniu/go-sdk/v7/storagev2/errors" +) + +// 调用 API 所用的请求 +type Request struct { + StartTime string // 检索日志的开始时间,日期格式按照 ISO8601 标准,并使用 UTC 时间 + EndTime string // 检索日志的结束时间,日期格式按照 ISO8601 标准,并使用 UTC 时间 + ServiceName string // 服务名称,参考 https://developer.qiniu.com/af/12434/audit-log-events + EventNames []string // 事件名称集合,参考 https://developer.qiniu.com/af/12434/audit-log-events + PrincipalId string // 请求者的 ID,参考 https://developer.qiniu.com/af/manual/12433/audit-log-object + AccessKeyId string // 请求身份所属的 AccessKey ID + Limit string // 允许返回的最大结果数目,取值范围:1~50,不传值默认为:20 + NextMark string // 用于请求下一页检索的结果 + Credentials credentials.CredentialsProvider // 鉴权参数,用于生成鉴权凭证,如果为空,则使用 HTTPClientOptions 中的 CredentialsProvider +} + +// 获取 API 所用的响应 +type Response struct { + NextMark string // 用于请求下一页检索的结果 + AuditLogInfos LogInfos // 日志集合 +} + +// 请求者的身份信息 +type UserIdentity struct { + AccountId string // 当前请求所属的七牛云账号 ID + PrincipalId string // 当前请求者的 ID,需结合 principal_type 来确认请求者身份 + PrincipalType string // 请求者身份类型,仅支持 UNKNOWN 表示未知,ROOT_USER 表示七牛云账户 ID,IAM_USER 表示 IAM 子账户 ID,QINIU_ACCOUNT 表示当七牛云账号跨账号操作时,记录七牛云账号 ID + AccessKeyId string // 当前请求身份所属的 AccessKey ID +} + +// 返回的请求者的身份信息 +type UserIdentify = UserIdentity +type jsonUserIdentity struct { + AccountId string `json:"account_id"` // 当前请求所属的七牛云账号 ID + PrincipalId string `json:"principal_id"` // 当前请求者的 ID,需结合 principal_type 来确认请求者身份 + PrincipalType string `json:"principal_type"` // 请求者身份类型,仅支持 UNKNOWN 表示未知,ROOT_USER 表示七牛云账户 ID,IAM_USER 表示 IAM 子账户 ID,QINIU_ACCOUNT 表示当七牛云账号跨账号操作时,记录七牛云账号 ID + AccessKeyId string `json:"access_key_id"` // 当前请求身份所属的 AccessKey ID +} + +func (j *UserIdentity) MarshalJSON() ([]byte, error) { + if err := j.validate(); err != nil { + return nil, err + } + return json.Marshal(&jsonUserIdentity{AccountId: j.AccountId, PrincipalId: j.PrincipalId, PrincipalType: j.PrincipalType, AccessKeyId: j.AccessKeyId}) +} +func (j *UserIdentity) UnmarshalJSON(data []byte) error { + var nj jsonUserIdentity + if err := json.Unmarshal(data, &nj); err != nil { + return err + } + j.AccountId = nj.AccountId + j.PrincipalId = nj.PrincipalId + j.PrincipalType = nj.PrincipalType + j.AccessKeyId = nj.AccessKeyId + return nil +} +func (j *UserIdentity) validate() error { + if j.AccountId == "" { + return errors.MissingRequiredFieldError{Name: "AccountId"} + } + if j.PrincipalId == "" { + return errors.MissingRequiredFieldError{Name: "PrincipalId"} + } + if j.PrincipalType == "" { + return errors.MissingRequiredFieldError{Name: "PrincipalType"} + } + if j.AccessKeyId == "" { + return errors.MissingRequiredFieldError{Name: "AccessKeyId"} + } + return nil +} + +// 返回的操作的资源名称列表 +type ResourceNames = []string + +// 返回的审计日志 +type LogInfo struct { + EventId string // 日志 ID + EventType string // 事件类型,仅支持 UNKNOWN 表示未知,CONSOLE 表示控制台事件,API 表示 API 事件 + EventTime string // 事件发生时间(UTC 格式) + UserIdentity UserIdentify // 请求者的身份信息 + EventRw string // 读写类型,仅支持 UNKNOWN 表示未知,READ 表示读,WRITE 表示写 + ServiceName string // 服务名称 + EventName string // 事件名称 + SourceIp string // 源 IP 地址 + UserAgent string // 客户端代理 + ResourceNames ResourceNames // 操作的资源名称列表 + RequestId string // 请求 ID + RequestUrl string // 请求 URL + RequestParams string // 请求的输入参数 + ResponseData string // 请求的返回数据 + ResponseCode int64 // 请求的返回码 + ResponseMessage string // 请求的返回信息 + AdditionalEventData string // 额外备注信息 +} +type jsonLogInfo struct { + EventId string `json:"event_id"` // 日志 ID + EventType string `json:"event_type"` // 事件类型,仅支持 UNKNOWN 表示未知,CONSOLE 表示控制台事件,API 表示 API 事件 + EventTime string `json:"event_time"` // 事件发生时间(UTC 格式) + UserIdentity UserIdentify `json:"user_identity"` // 请求者的身份信息 + EventRw string `json:"event_rw"` // 读写类型,仅支持 UNKNOWN 表示未知,READ 表示读,WRITE 表示写 + ServiceName string `json:"service_name"` // 服务名称 + EventName string `json:"event_name"` // 事件名称 + SourceIp string `json:"source_ip"` // 源 IP 地址 + UserAgent string `json:"user_agent"` // 客户端代理 + ResourceNames ResourceNames `json:"resource_names"` // 操作的资源名称列表 + RequestId string `json:"request_id"` // 请求 ID + RequestUrl string `json:"request_url"` // 请求 URL + RequestParams string `json:"request_params"` // 请求的输入参数 + ResponseData string `json:"response_data"` // 请求的返回数据 + ResponseCode int64 `json:"response_code"` // 请求的返回码 + ResponseMessage string `json:"response_message"` // 请求的返回信息 + AdditionalEventData string `json:"additional_event_data"` // 额外备注信息 +} + +func (j *LogInfo) MarshalJSON() ([]byte, error) { + if err := j.validate(); err != nil { + return nil, err + } + return json.Marshal(&jsonLogInfo{EventId: j.EventId, EventType: j.EventType, EventTime: j.EventTime, UserIdentity: j.UserIdentity, EventRw: j.EventRw, ServiceName: j.ServiceName, EventName: j.EventName, SourceIp: j.SourceIp, UserAgent: j.UserAgent, ResourceNames: j.ResourceNames, RequestId: j.RequestId, RequestUrl: j.RequestUrl, RequestParams: j.RequestParams, ResponseData: j.ResponseData, ResponseCode: j.ResponseCode, ResponseMessage: j.ResponseMessage, AdditionalEventData: j.AdditionalEventData}) +} +func (j *LogInfo) UnmarshalJSON(data []byte) error { + var nj jsonLogInfo + if err := json.Unmarshal(data, &nj); err != nil { + return err + } + j.EventId = nj.EventId + j.EventType = nj.EventType + j.EventTime = nj.EventTime + j.UserIdentity = nj.UserIdentity + j.EventRw = nj.EventRw + j.ServiceName = nj.ServiceName + j.EventName = nj.EventName + j.SourceIp = nj.SourceIp + j.UserAgent = nj.UserAgent + j.ResourceNames = nj.ResourceNames + j.RequestId = nj.RequestId + j.RequestUrl = nj.RequestUrl + j.RequestParams = nj.RequestParams + j.ResponseData = nj.ResponseData + j.ResponseCode = nj.ResponseCode + j.ResponseMessage = nj.ResponseMessage + j.AdditionalEventData = nj.AdditionalEventData + return nil +} +func (j *LogInfo) validate() error { + if j.EventId == "" { + return errors.MissingRequiredFieldError{Name: "EventId"} + } + if j.EventType == "" { + return errors.MissingRequiredFieldError{Name: "EventType"} + } + if j.EventTime == "" { + return errors.MissingRequiredFieldError{Name: "EventTime"} + } + if err := j.UserIdentity.validate(); err != nil { + return err + } + if j.EventRw == "" { + return errors.MissingRequiredFieldError{Name: "EventRw"} + } + if j.ServiceName == "" { + return errors.MissingRequiredFieldError{Name: "ServiceName"} + } + if j.EventName == "" { + return errors.MissingRequiredFieldError{Name: "EventName"} + } + if j.SourceIp == "" { + return errors.MissingRequiredFieldError{Name: "SourceIp"} + } + if j.UserAgent == "" { + return errors.MissingRequiredFieldError{Name: "UserAgent"} + } + if len(j.ResourceNames) == 0 { + return errors.MissingRequiredFieldError{Name: "ResourceNames"} + } + if j.RequestId == "" { + return errors.MissingRequiredFieldError{Name: "RequestId"} + } + if j.RequestUrl == "" { + return errors.MissingRequiredFieldError{Name: "RequestUrl"} + } + if j.RequestParams == "" { + return errors.MissingRequiredFieldError{Name: "RequestParams"} + } + if j.ResponseData == "" { + return errors.MissingRequiredFieldError{Name: "ResponseData"} + } + if j.ResponseCode == 0 { + return errors.MissingRequiredFieldError{Name: "ResponseCode"} + } + if j.ResponseMessage == "" { + return errors.MissingRequiredFieldError{Name: "ResponseMessage"} + } + if j.AdditionalEventData == "" { + return errors.MissingRequiredFieldError{Name: "AdditionalEventData"} + } + return nil +} + +// 返回的审计日志列表 +type LogInfos = []LogInfo +type jsonResponse struct { + NextMark string `json:"next_mark,omitempty"` // 用于请求下一页检索的结果 + AuditLogInfos LogInfos `json:"audit_log_infos"` // 日志集合 +} + +func (j *Response) MarshalJSON() ([]byte, error) { + if err := j.validate(); err != nil { + return nil, err + } + return json.Marshal(&jsonResponse{NextMark: j.NextMark, AuditLogInfos: j.AuditLogInfos}) +} +func (j *Response) UnmarshalJSON(data []byte) error { + var nj jsonResponse + if err := json.Unmarshal(data, &nj); err != nil { + return err + } + j.NextMark = nj.NextMark + j.AuditLogInfos = nj.AuditLogInfos + return nil +} +func (j *Response) validate() error { + if len(j.AuditLogInfos) == 0 { + return errors.MissingRequiredFieldError{Name: "AuditLogInfos"} + } + for _, value := range j.AuditLogInfos { + if err := value.validate(); err != nil { + return err + } + } + return nil +} diff --git a/audit/doc.go b/audit/doc.go new file mode 100644 index 00000000..cf67c1dc --- /dev/null +++ b/audit/doc.go @@ -0,0 +1,5 @@ +// audit 包提供了账号审计等功能。 +package audit + +//go:generate go run ../internal/api-generator -- --api-specs=../api-specs/audit --output=apis/ --struct-name=Audit --api-package=github.com/qiniu/go-sdk/v7/audit/apis +//go:generate go build ./apis/...