This commit is contained in:
@@ -27,31 +27,31 @@ type SubjectActivityFilter struct {
|
||||
|
||||
// SubjectActivityPage 是不包含平台调查字段的代理资源活动分页结果。
|
||||
type SubjectActivityPage struct {
|
||||
Resource SubjectResourceSummary `json:"resource"`
|
||||
Total int64 `json:"total"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"page_size"`
|
||||
Items []SubjectActivity `json:"items"`
|
||||
Retention retentionquery.Info `json:"retention"`
|
||||
Resource SubjectResourceSummary `json:"resource" description:"已完成授权校验的目标资源"`
|
||||
Total int64 `json:"total" description:"主体可见活动总数"`
|
||||
Page int `json:"page" description:"当前页码"`
|
||||
PageSize int `json:"page_size" description:"每页数量"`
|
||||
Items []SubjectActivity `json:"items" description:"不包含平台内部调查字段的安全活动列表"`
|
||||
Retention retentionquery.Info `json:"retention" description:"在线查询留存边界"`
|
||||
}
|
||||
|
||||
// SubjectActivity 是写入时已生成的主体安全活动投影。
|
||||
type SubjectActivity struct {
|
||||
ActionCode string `json:"action_code"`
|
||||
ActionName string `json:"action_name"`
|
||||
SubjectSummary string `json:"subject_summary"`
|
||||
SubjectData map[string]any `json:"subject_data"`
|
||||
Result string `json:"result"`
|
||||
OccurredAt time.Time `json:"occurred_at"`
|
||||
RelatedResources []SubjectResourceSummary `json:"related_resources"`
|
||||
ActionCode string `json:"action_code" description:"稳定动作编码"`
|
||||
ActionName string `json:"action_name" description:"action_code对应的中文展示名称"`
|
||||
SubjectSummary string `json:"subject_summary" description:"写入时生成的主体安全摘要"`
|
||||
SubjectData map[string]any `json:"subject_data" description:"写入时生成的主体安全业务字段,不包含平台before/after或内部原因"`
|
||||
Result string `json:"result" enum:"success,failed,denied,partial,unknown" description:"活动结果稳定编码"`
|
||||
OccurredAt time.Time `json:"occurred_at" description:"业务事实发生时间"`
|
||||
RelatedResources []SubjectResourceSummary `json:"related_resources" description:"当前主体授权范围内的相关资源摘要"`
|
||||
}
|
||||
|
||||
// SubjectResourceSummary 是主体活动允许公开的资源摘要。
|
||||
type SubjectResourceSummary struct {
|
||||
ResourceType string `json:"resource_type"`
|
||||
ResourceID string `json:"resource_id"`
|
||||
ResourceKey string `json:"resource_key"`
|
||||
DisplayName string `json:"display_name"`
|
||||
ResourceType string `json:"resource_type" description:"资源类型稳定编码"`
|
||||
ResourceID string `json:"resource_id" description:"资源内部稳定ID;主体前端不据此调用平台审计接口"`
|
||||
ResourceKey string `json:"resource_key" description:"资源业务稳定Key"`
|
||||
DisplayName string `json:"display_name" description:"资源安全展示名称"`
|
||||
}
|
||||
|
||||
type subjectTarget struct {
|
||||
@@ -60,14 +60,15 @@ type subjectTarget struct {
|
||||
}
|
||||
|
||||
type subjectActivityRow struct {
|
||||
ID uint
|
||||
ActionCode string
|
||||
ActionName string
|
||||
Result string
|
||||
OccurredAt time.Time
|
||||
SubjectSummary string
|
||||
SubjectData datatypes.JSON
|
||||
TargetResourceID uint
|
||||
ID uint
|
||||
ActionCode string
|
||||
ActionName string
|
||||
Result string
|
||||
OccurredAt time.Time
|
||||
SubjectVisibility string
|
||||
SubjectSummary string
|
||||
SubjectData datatypes.JSON
|
||||
TargetResourceID uint
|
||||
}
|
||||
|
||||
type subjectResourceAuthorizer func(context.Context, []model.AuditEventResource) (map[string]bool, error)
|
||||
@@ -78,9 +79,12 @@ func (q *Query) AgentResourceActivities(ctx context.Context, filter SubjectActiv
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if filter.Identifier == "" || !agentActivityResourceType(filter.ResourceType) || filter.Page < 0 || filter.PageSize < 0 || filter.PageSize > constants.MaxPageSize {
|
||||
if filter.Identifier == "" || filter.Page < 0 || filter.PageSize < 0 || filter.PageSize > constants.MaxPageSize {
|
||||
return nil, errors.New(errors.CodeInvalidParam)
|
||||
}
|
||||
if !agentActivityResourceType(filter.ResourceType) {
|
||||
return nil, errors.New(errors.CodeForbidden, "无权限操作该资源或资源不存在")
|
||||
}
|
||||
filter.Page, filter.PageSize = normalizePage(filter.Page, filter.PageSize)
|
||||
retention, err := retentionquery.Load(ctx, q.db, retentionquery.SourceAudit)
|
||||
if err != nil {
|
||||
@@ -108,9 +112,12 @@ func (q *Query) EnterpriseResourceActivities(ctx context.Context, filter Subject
|
||||
if enterpriseID == 0 {
|
||||
return nil, errors.New(errors.CodeForbidden, "无权限操作该资源或资源不存在")
|
||||
}
|
||||
if filter.Identifier == "" || !enterpriseActivityResourceType(filter.ResourceType) || filter.Page < 0 || filter.PageSize < 0 || filter.PageSize > constants.MaxPageSize {
|
||||
if filter.Identifier == "" || filter.Page < 0 || filter.PageSize < 0 || filter.PageSize > constants.MaxPageSize {
|
||||
return nil, errors.New(errors.CodeInvalidParam)
|
||||
}
|
||||
if !enterpriseActivityResourceType(filter.ResourceType) {
|
||||
return nil, errors.New(errors.CodeForbidden, "无权限操作该资源或资源不存在")
|
||||
}
|
||||
filter.Page, filter.PageSize = normalizePage(filter.Page, filter.PageSize)
|
||||
retention, err := retentionquery.Load(ctx, q.db, retentionquery.SourceAudit)
|
||||
if err != nil {
|
||||
@@ -142,13 +149,13 @@ func (q *Query) subjectActivitiesForTarget(ctx context.Context, filter SubjectAc
|
||||
}
|
||||
|
||||
rows := make([]subjectActivityRow, 0, filter.PageSize)
|
||||
if err := base.Select("tb_audit_event.id, action_code, action_name, result, occurred_at, target.subject_summary, target.subject_data, target.id AS target_resource_id").
|
||||
if err := base.Select("tb_audit_event.id, action_code, action_name, result, occurred_at, target.subject_visibility, target.subject_summary, target.subject_data, target.id AS target_resource_id").
|
||||
Joins("JOIN tb_audit_event_resource AS target ON target.audit_event_id = tb_audit_event.id AND target.resource_type = ? AND target.resource_id = ?", filter.ResourceType, target.id).
|
||||
Where("target.subject_visibility IN ?", []string{constants.AuditSubjectResult, constants.AuditSubjectDetail}).
|
||||
Order("occurred_at DESC, tb_audit_event.id DESC").Offset((filter.Page - 1) * filter.PageSize).Limit(filter.PageSize).Scan(&rows).Error; err != nil {
|
||||
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询代理资源活动失败")
|
||||
}
|
||||
items, err := q.projectSubjectActivities(ctx, rows, authorize)
|
||||
items, err := q.projectSubjectActivities(ctx, rows, target, authorize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -265,24 +272,28 @@ func newSubjectTarget(resourceType string, id uint, key, name string) subjectTar
|
||||
return subjectTarget{summary: SubjectResourceSummary{ResourceType: resourceType, ResourceID: resourceID, ResourceKey: key, DisplayName: name}, id: resourceID}
|
||||
}
|
||||
|
||||
func (q *Query) projectSubjectActivities(ctx context.Context, rows []subjectActivityRow, authorize subjectResourceAuthorizer) ([]SubjectActivity, error) {
|
||||
func (q *Query) projectSubjectActivities(ctx context.Context, rows []subjectActivityRow, target subjectTarget, authorize subjectResourceAuthorizer) ([]SubjectActivity, error) {
|
||||
items := make([]SubjectActivity, 0, len(rows))
|
||||
if len(rows) == 0 {
|
||||
return items, nil
|
||||
}
|
||||
eventIDs := make([]uint, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
eventIDs = append(eventIDs, row.ID)
|
||||
}
|
||||
var resources []model.AuditEventResource
|
||||
if err := q.db.WithContext(ctx).Where("audit_event_id IN ? AND subject_visibility IN ?", eventIDs, []string{constants.AuditSubjectResult, constants.AuditSubjectDetail}).
|
||||
Order("audit_event_id ASC, sort_order ASC, id ASC").Find(&resources).Error; err != nil {
|
||||
return nil, errors.Wrap(errors.CodeDatabaseError, err, "批量查询主体可见关联资源失败")
|
||||
resources := make([]model.AuditEventResource, 0)
|
||||
if len(eventIDs) > 0 {
|
||||
if err := q.db.WithContext(ctx).Where("audit_event_id IN ? AND subject_visibility IN ?", eventIDs, []string{constants.AuditSubjectResult, constants.AuditSubjectDetail}).
|
||||
Order("audit_event_id ASC, sort_order ASC, id ASC").Find(&resources).Error; err != nil {
|
||||
return nil, errors.Wrap(errors.CodeDatabaseError, err, "批量查询主体可见关联资源失败")
|
||||
}
|
||||
}
|
||||
allowed, err := authorize(ctx, resources)
|
||||
targetResourceID := target.id
|
||||
authorizationResources := append(resources, model.AuditEventResource{ResourceType: target.summary.ResourceType, ResourceID: &targetResourceID})
|
||||
allowed, err := authorize(ctx, authorizationResources)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !allowed[resourceAccessKey(target.summary.ResourceType, target.id)] {
|
||||
return nil, errors.New(errors.CodeForbidden, "无权限操作该资源或资源不存在")
|
||||
}
|
||||
related := make(map[uint][]SubjectResourceSummary, len(rows))
|
||||
for _, resource := range resources {
|
||||
if resource.ResourceID == nil || !allowed[resourceAccessKey(resource.ResourceType, *resource.ResourceID)] {
|
||||
@@ -294,9 +305,12 @@ func (q *Query) projectSubjectActivities(ctx context.Context, rows []subjectActi
|
||||
})
|
||||
}
|
||||
for _, row := range rows {
|
||||
data, err := decodeObject(row.SubjectData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
data := map[string]any{}
|
||||
if row.SubjectVisibility == constants.AuditSubjectDetail {
|
||||
data, err = decodeObject(row.SubjectData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
items = append(items, SubjectActivity{ActionCode: row.ActionCode, ActionName: row.ActionName,
|
||||
SubjectSummary: row.SubjectSummary, SubjectData: data, Result: row.Result,
|
||||
|
||||
Reference in New Issue
Block a user