182 lines
3.5 KiB
Go
182 lines
3.5 KiB
Go
package telemetry
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/beckn-one/beckn-onix/pkg/log"
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
type auditFieldsRules struct {
|
|
AuditRules map[string][]string `yaml:"auditRules"`
|
|
}
|
|
|
|
var (
|
|
auditRules = map[string][]string{}
|
|
auditRulesMutex sync.RWMutex
|
|
)
|
|
|
|
func LoadAuditFieldRules(ctx context.Context, configPath string) error {
|
|
|
|
if strings.TrimSpace(configPath) == "" {
|
|
err := fmt.Errorf("config file path is empty")
|
|
log.Error(ctx, err, "there are no audit rules defined")
|
|
return err
|
|
}
|
|
|
|
data, err := os.ReadFile(configPath)
|
|
if err != nil {
|
|
log.Error(ctx, err, "failed to read audit rules file")
|
|
return err
|
|
}
|
|
|
|
var config auditFieldsRules
|
|
if err := yaml.Unmarshal(data, &config); err != nil {
|
|
log.Error(ctx, err, "failed to parse audit rules file")
|
|
return err
|
|
}
|
|
|
|
if config.AuditRules == nil {
|
|
log.Warn(ctx, "audit rules are not defined")
|
|
config.AuditRules = map[string][]string{}
|
|
}
|
|
|
|
auditRulesMutex.Lock()
|
|
auditRules = config.AuditRules
|
|
auditRulesMutex.Unlock()
|
|
log.Info(ctx, "audit rules loaded")
|
|
return nil
|
|
}
|
|
|
|
func selectAuditPayload(ctx context.Context, body []byte) []byte {
|
|
|
|
var root map[string]interface{}
|
|
if err := json.Unmarshal(body, &root); err != nil {
|
|
log.Warn(ctx, "failed to unmarshal audit payload ")
|
|
return nil
|
|
}
|
|
|
|
action := ""
|
|
if c, ok := root["context"].(map[string]interface{}); ok {
|
|
if v, ok := c["action"].(string); ok {
|
|
action = strings.TrimSpace(v)
|
|
}
|
|
}
|
|
|
|
fields := getFieldForAction(ctx, action)
|
|
if len(fields) == 0 {
|
|
return nil
|
|
}
|
|
|
|
out := map[string]interface{}{}
|
|
for _, field := range fields {
|
|
parts := strings.Split(field, ".")
|
|
partial, ok := projectPath(root, parts)
|
|
if !ok {
|
|
continue
|
|
}
|
|
merged := deepMerge(out, partial)
|
|
if m, ok := merged.(map[string]interface{}); ok {
|
|
out = m
|
|
}
|
|
}
|
|
|
|
body, err := json.Marshal(out)
|
|
if err != nil {
|
|
log.Warn(ctx, "failed to marshal audit payload")
|
|
return nil
|
|
}
|
|
return body
|
|
}
|
|
|
|
func getFieldForAction(ctx context.Context, action string) []string {
|
|
auditRulesMutex.RLock()
|
|
defer auditRulesMutex.RUnlock()
|
|
|
|
if action != "" {
|
|
if fields, ok := auditRules[action]; ok && len(fields) > 0 {
|
|
return fields
|
|
}
|
|
}
|
|
|
|
log.Warn(ctx, "audit rules are not defined for this action send default")
|
|
return auditRules["default"]
|
|
}
|
|
|
|
func projectPath(cur interface{}, parts []string) (interface{}, bool) {
|
|
if len(parts) == 0 {
|
|
return cur, true
|
|
}
|
|
|
|
switch node := cur.(type) {
|
|
case map[string]interface{}:
|
|
next, ok := node[parts[0]]
|
|
if !ok {
|
|
return nil, false
|
|
}
|
|
child, ok := projectPath(next, parts[1:])
|
|
if !ok {
|
|
return nil, false
|
|
}
|
|
return map[string]interface{}{parts[0]: child}, true
|
|
|
|
case []interface{}:
|
|
out := make([]interface{}, 0, len(node))
|
|
found := false
|
|
|
|
for _, n := range node {
|
|
child, ok := projectPath(n, parts)
|
|
if ok {
|
|
out = append(out, child)
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
return nil, false
|
|
}
|
|
return out, true
|
|
|
|
default:
|
|
return nil, false
|
|
}
|
|
}
|
|
func deepMerge(dst, src interface{}) interface{} {
|
|
if dst == nil {
|
|
return src
|
|
}
|
|
|
|
dm, dok := dst.(map[string]interface{})
|
|
sm, sok := src.(map[string]interface{})
|
|
if dok && sok {
|
|
for k, sv := range sm {
|
|
if dv, ok := dm[k]; ok {
|
|
dm[k] = deepMerge(dv, sv)
|
|
} else {
|
|
dm[k] = sv
|
|
}
|
|
}
|
|
return dm
|
|
}
|
|
|
|
da, dok := dst.([]interface{})
|
|
sa, sok := src.([]interface{})
|
|
if dok && sok {
|
|
if len(da) < len(sa) {
|
|
ext := make([]interface{}, len(sa)-len(da))
|
|
da = append(da, ext...)
|
|
}
|
|
|
|
for i := range sa {
|
|
da[i] = deepMerge(da[i], sa[i])
|
|
}
|
|
return da
|
|
}
|
|
|
|
return src
|
|
}
|