105 lines
3.5 KiB
Go
105 lines
3.5 KiB
Go
package config
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
ErrAuditQueryUnavailable = errors.New("configuration audit query unavailable")
|
|
ErrInvalidAuditQuery = errors.New("configuration audit query is invalid")
|
|
ErrInvalidAuditFormat = errors.New("configuration audit export format is invalid")
|
|
ErrAuditNotFound = errors.New("configuration audit event not found")
|
|
)
|
|
|
|
// ValidateConfigAuditQuery 校验审计查询条件,拒绝未知或越界参数。
|
|
func ValidateConfigAuditQuery(query ConfigAuditQuery) error {
|
|
if query.ID != "" && !validAuditEventID(query.ID) {
|
|
return ErrInvalidAuditQuery
|
|
}
|
|
if query.Limit < 0 || query.Limit > 1000 {
|
|
return ErrInvalidAuditQuery
|
|
}
|
|
if !query.From.IsZero() && !query.To.IsZero() && query.From.After(query.To) {
|
|
return ErrInvalidAuditQuery
|
|
}
|
|
if len(query.ActorID) > 128 || strings.ContainsAny(query.ActorID, "\r\n") {
|
|
return ErrInvalidAuditQuery
|
|
}
|
|
if query.Operation != "" && query.Operation != ConfigOperationUpdate && query.Operation != ConfigOperationRollback && query.Operation != ConfigOperationMigration {
|
|
return ErrInvalidAuditQuery
|
|
}
|
|
if query.Section != "" && query.Section != SectionRoot && query.Section != SectionMail && query.Section != SectionSMTP && query.Section != SectionEmail && query.Section != SectionSSL && query.Section != SectionPlugins {
|
|
return ErrInvalidAuditQuery
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AuditQuery 读取配置审计的只读接口,写入器不支持查询时明确拒绝。
|
|
func (m *ConfigManager) AuditQuery(ctx context.Context, query ConfigAuditQuery) ([]ConfigAuditEvent, error) {
|
|
if err := ValidateConfigAuditQuery(query); err != nil {
|
|
return nil, err
|
|
}
|
|
if m == nil || m.security.Audit == nil {
|
|
return nil, ErrAuditQueryUnavailable
|
|
}
|
|
reader, ok := m.security.Audit.(interface {
|
|
Query(context.Context, ConfigAuditQuery) ([]ConfigAuditEvent, error)
|
|
})
|
|
if !ok {
|
|
return nil, ErrAuditQueryUnavailable
|
|
}
|
|
return reader.Query(ctx, query)
|
|
}
|
|
|
|
// ExportAuditJSON 导出脱敏配置审计;导出内容由审计存储实现生成。
|
|
func (m *ConfigManager) ExportAuditJSON(ctx context.Context, query ConfigAuditQuery) ([]byte, error) {
|
|
if err := ValidateConfigAuditQuery(query); err != nil {
|
|
return nil, err
|
|
}
|
|
if m == nil || m.security.Audit == nil {
|
|
return nil, ErrAuditQueryUnavailable
|
|
}
|
|
reader, ok := m.security.Audit.(interface {
|
|
ExportJSON(context.Context, ConfigAuditQuery) ([]byte, error)
|
|
})
|
|
if !ok {
|
|
return nil, ErrAuditQueryUnavailable
|
|
}
|
|
return reader.ExportJSON(ctx, query)
|
|
}
|
|
|
|
// ExportAuditCSV 导出脱敏配置审计;导出内容由审计存储实现生成。
|
|
func (m *ConfigManager) ExportAuditCSV(ctx context.Context, query ConfigAuditQuery) ([]byte, error) {
|
|
if err := ValidateConfigAuditQuery(query); err != nil {
|
|
return nil, err
|
|
}
|
|
if m == nil || m.security.Audit == nil {
|
|
return nil, ErrAuditQueryUnavailable
|
|
}
|
|
reader, ok := m.security.Audit.(interface {
|
|
ExportCSV(context.Context, ConfigAuditQuery) ([]byte, error)
|
|
})
|
|
if !ok {
|
|
return nil, ErrAuditQueryUnavailable
|
|
}
|
|
return reader.ExportCSV(ctx, query)
|
|
}
|
|
|
|
// AuditDetail 按稳定标识查询一条配置审计事件。
|
|
func (m *ConfigManager) AuditDetail(ctx context.Context, id string) (ConfigAuditEvent, error) {
|
|
id = strings.TrimSpace(id)
|
|
if !validAuditEventID(id) {
|
|
return ConfigAuditEvent{}, ErrInvalidAuditQuery
|
|
}
|
|
items, err := m.AuditQuery(ctx, ConfigAuditQuery{ID: id, Limit: 1})
|
|
if err != nil {
|
|
return ConfigAuditEvent{}, err
|
|
}
|
|
if len(items) == 0 {
|
|
return ConfigAuditEvent{}, ErrAuditNotFound
|
|
}
|
|
return items[0], nil
|
|
}
|