Added *regex filter

This commit is contained in:
Trial97
2021-05-20 14:37:55 +03:00
committed by Dan Christian Bogos
parent 3f06d822d6
commit a842fc67bd
4 changed files with 156 additions and 39 deletions

View File

@@ -19,6 +19,7 @@ import (
"fmt"
"net"
"reflect"
"regexp"
"strings"
"time"
@@ -224,19 +225,20 @@ var supportedFiltersType utils.StringSet = utils.NewStringSet([]string{
utils.MetaTimings, utils.MetaRSR, utils.MetaDestinations,
utils.MetaEmpty, utils.MetaExists, utils.MetaLessThan, utils.MetaLessOrEqual,
utils.MetaGreaterThan, utils.MetaGreaterOrEqual, utils.MetaEqual,
utils.MetaNotEqual, utils.MetaIPNet, utils.MetaAPIBan,
utils.MetaActivationInterval})
utils.MetaIPNet, utils.MetaAPIBan, utils.MetaActivationInterval,
utils.MetaRegex})
var needsFieldName utils.StringSet = utils.NewStringSet([]string{
utils.MetaString, utils.MetaPrefix, utils.MetaSuffix,
utils.MetaTimings, utils.MetaRSR, utils.MetaDestinations, utils.MetaLessThan,
utils.MetaEmpty, utils.MetaExists, utils.MetaLessOrEqual, utils.MetaGreaterThan,
utils.MetaGreaterOrEqual, utils.MetaEqual, utils.MetaNotEqual, utils.MetaIPNet, utils.MetaAPIBan,
utils.MetaActivationInterval})
utils.MetaGreaterOrEqual, utils.MetaEqual, utils.MetaIPNet, utils.MetaAPIBan,
utils.MetaActivationInterval,
utils.MetaRegex})
var needsValues utils.StringSet = utils.NewStringSet([]string{utils.MetaString, utils.MetaPrefix,
utils.MetaSuffix, utils.MetaTimings, utils.MetaRSR, utils.MetaDestinations,
utils.MetaLessThan, utils.MetaLessOrEqual, utils.MetaGreaterThan, utils.MetaGreaterOrEqual,
utils.MetaEqual, utils.MetaNotEqual, utils.MetaIPNet, utils.MetaAPIBan,
utils.MetaActivationInterval})
utils.MetaEqual, utils.MetaIPNet, utils.MetaAPIBan, utils.MetaActivationInterval,
utils.MetaRegex})
// NewFilterRule returns a new filter
func NewFilterRule(rfType, fieldName string, vals []string) (*FilterRule, error) {
@@ -270,55 +272,47 @@ func NewFilterRule(rfType, fieldName string, vals []string) (*FilterRule, error)
// FilterRule filters requests coming into various places
// Pass rule: default negative, one matching rule should pass the filter
type FilterRule struct {
Type string // Filter type (*string, *timing, *rsr_filters, *stats, *lt, *lte, *gt, *gte)
Element string // Name of the field providing us the Values to check (used in case of some )
Values []string // Filter definition
rsrValues config.RSRParsers // Cache here the
rsrElement *config.RSRParser // Cache here the
rsrFilters utils.RSRFilters // Cache here the RSRFilter Values
negative *bool
Type string // Filter type (*string, *timing, *rsr_filters, *stats, *lt, *lte, *gt, *gte)
Element string // Name of the field providing us the Values to check (used in case of some )
Values []string // Filter definition
rsrValues config.RSRParsers // Cache here the
rsrElement *config.RSRParser // Cache here the
rsrFilters utils.RSRFilters // Cache here the RSRFilter Values
regexValues []*regexp.Regexp
negative *bool
}
// CompileValues compiles RSR fields
func (fltr *FilterRule) CompileValues() (err error) {
switch fltr.Type {
case utils.MetaRegex, utils.MetaNotRegex:
fltr.regexValues = make([]*regexp.Regexp, len(fltr.Values))
for i, val := range fltr.Values {
if fltr.regexValues[i], err = regexp.Compile(val); err != nil {
return
}
}
case utils.MetaRSR, utils.MetaNotRSR:
if fltr.rsrFilters, err = utils.ParseRSRFiltersFromSlice(fltr.Values); err != nil {
return
}
if fltr.rsrElement, err = config.NewRSRParser(fltr.Element); err != nil {
return
} else if fltr.rsrElement == nil {
return fmt.Errorf("emtpy RSRParser in rule: <%s>", fltr.Element)
}
case utils.MetaExists, utils.MetaNotExists, utils.MetaEmpty, utils.MetaNotEmpty: // only the element is builded
if fltr.rsrElement, err = config.NewRSRParser(fltr.Element); err != nil {
return
} else if fltr.rsrElement == nil {
return fmt.Errorf("emtpy RSRParser in rule: <%s>", fltr.Element)
}
case utils.MetaActivationInterval, utils.MetaNotActivationInterval:
if fltr.rsrElement, err = config.NewRSRParser(fltr.Element); err != nil {
return
} else if fltr.rsrElement == nil {
return fmt.Errorf("emtpy RSRParser in rule: <%s>", fltr.Element)
}
for _, strVal := range fltr.Values {
rsrPrsr, err := config.NewRSRParser(strVal)
if err != nil {
return err
fltr.rsrValues = make(config.RSRParsers, len(fltr.Values))
for i, strVal := range fltr.Values {
if fltr.rsrValues[i], err = config.NewRSRParser(strVal); err != nil {
return
}
fltr.rsrValues = append(fltr.rsrValues, rsrPrsr)
}
default:
if fltr.rsrValues, err = config.NewRSRParsersFromSlice(fltr.Values); err != nil {
return
}
if fltr.rsrElement, err = config.NewRSRParser(fltr.Element); err != nil {
return
} else if fltr.rsrElement == nil {
return fmt.Errorf("emtpy RSRParser in rule: <%s>", fltr.Element)
}
}
if fltr.rsrElement, err = config.NewRSRParser(fltr.Element); err != nil {
return
} else if fltr.rsrElement == nil {
return fmt.Errorf("emtpy RSRParser in rule: <%s>", fltr.Element)
}
return
}
@@ -356,6 +350,8 @@ func (fltr *FilterRule) Pass(dDP utils.DataProvider) (result bool, err error) {
result, err = fltr.passAPIBan(dDP)
case utils.MetaActivationInterval, utils.MetaNotActivationInterval:
result, err = fltr.passActivationInterval(dDP)
case utils.MetaRegex, utils.MetaNotRegex:
result, err = fltr.passRegex(dDP)
default:
err = utils.ErrPrefixNotErrNotImplemented(fltr.Type)
}
@@ -740,3 +736,19 @@ func CheckFilter(fltr *Filter) (err error) {
}
return nil
}
func (fltr *FilterRule) passRegex(dDP utils.DataProvider) (bool, error) {
strVal, err := fltr.rsrElement.ParseDataProvider(dDP)
if err != nil {
if err == utils.ErrNotFound {
return false, nil
}
return false, err
}
for _, val := range fltr.regexValues {
if val.MatchString(strVal) {
return true, nil
}
}
return false, nil
}

View File

@@ -83,6 +83,61 @@ func TestFilterPassString(t *testing.T) {
}
}
func TestFilterPassRegex(t *testing.T) {
cd := &CallDescriptor{
Category: "call",
Tenant: "cgrates.org",
Subject: "dan",
Destination: "+4986517174963",
TimeStart: time.Date(2013, time.October, 7, 14, 50, 0, 0, time.UTC),
TimeEnd: time.Date(2013, time.October, 7, 14, 52, 12, 0, time.UTC),
DurationIndex: 132 * time.Second,
ExtraFields: map[string]string{"navigation": "off"},
}
rf := &FilterRule{Type: utils.MetaRegex,
Element: "~Category", Values: []string{"^call$"}}
if err := rf.CompileValues(); err != nil {
t.Fatal(err)
}
if passes, err := rf.passRegex(cd); err != nil {
t.Error(err)
} else if !passes {
t.Error("Not passes filter")
}
rf = &FilterRule{Type: utils.MetaRegex,
Element: "~Category", Values: []string{"cal$"}}
if err := rf.CompileValues(); err != nil {
t.Fatal(err)
}
if passes, err := rf.passRegex(cd); err != nil {
t.Error(err)
} else if passes {
t.Error("Filter passes")
}
//not
rf = &FilterRule{Type: utils.MetaNotRegex,
Element: "~Category", Values: []string{"^call$"}}
if err := rf.CompileValues(); err != nil {
t.Fatal(err)
}
if passes, err := rf.Pass(cd); err != nil {
t.Error(err)
} else if passes {
t.Error("Filter passes")
}
rf = &FilterRule{Type: utils.MetaNotRegex,
Element: "~Category", Values: []string{"cal$"}}
if err := rf.CompileValues(); err != nil {
t.Fatal(err)
}
if passes, err := rf.Pass(cd); err != nil {
t.Error(err)
} else if !passes {
t.Error("Not passes filter")
}
}
func TestFilterPassEmpty(t *testing.T) {
cd := &CallDescriptor{
Category: "",
@@ -678,6 +733,18 @@ func TestFilterNewRequestFilter(t *testing.T) {
if !reflect.DeepEqual(erf, rf) {
t.Errorf("Expecting: %+v, received: %+v", erf, rf)
}
rf, err = NewFilterRule(utils.MetaRegex, "~MetaRegex", []string{"Regex"})
if err != nil {
t.Errorf("Error: %+v", err)
}
erf = &FilterRule{Type: utils.MetaRegex, Element: "~MetaRegex", Values: []string{"Regex"}, negative: utils.BoolPointer(false)}
if err = erf.CompileValues(); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(erf, rf) {
t.Errorf("Expecting: %+v, received: %+v", erf, rf)
}
}
func TestInlineFilterPassFiltersForEvent(t *testing.T) {
@@ -865,6 +932,42 @@ func TestInlineFilterPassFiltersForEvent(t *testing.T) {
} else if !pass {
t.Errorf("For NewKey expecting: %+v, received: %+v", true, pass)
}
failEvent = map[string]interface{}{
"Account": "1001",
}
passEvent = map[string]interface{}{
"Account": "1007",
}
fEv = utils.MapStorage{}
fEv.Set([]string{utils.MetaReq}, failEvent)
if pass, err := filterS.Pass("cgrates.org",
[]string{"*regex:~*req.Account:^1007:error"}, fEv); err != nil {
t.Error(err)
} else if pass {
t.Errorf("Expecting: %+v, received: %+v", false, pass)
}
if pass, err := filterS.Pass("cgrates.org",
[]string{"*regex:~*req.Account:\\d{3}7"}, fEv); err != nil {
t.Errorf(err.Error())
} else if pass {
t.Errorf("Expecting: %+v, received: %+v", false, pass)
}
pEv = utils.MapStorage{}
pEv.Set([]string{utils.MetaReq}, passEvent)
if pass, err := filterS.Pass("cgrates.org",
[]string{"*regex:~*req.Account:\\d{3}7"}, pEv); err != nil {
t.Errorf(err.Error())
} else if !pass {
t.Errorf("Expecting: %+v, received: %+v", true, pass)
}
//not
if pass, err := filterS.Pass("cgrates.org",
[]string{"*notregex:~*req.Account:\\d{3}7"}, pEv); err != nil {
t.Errorf(err.Error())
} else if pass {
t.Errorf("Expecting: %+v, received: %+v", false, pass)
}
}
func TestPassFiltersForEventWithEmptyFilter(t *testing.T) {

View File

@@ -164,7 +164,7 @@ cgrates (0.11.0~dev) UNRELEASED; urgency=medium
* [DispatcherS] Added any_subsyste config to control the matching dispatchers
* [StatS] AverageCallCost and TotalCallCost now returns error for negative Cost field
* [SessionS] The sessions are no longer terminated on shutdown if the replication_conns are set
* [FilterS] Added *regex filter
-- DanB <danb@cgrates.org> Wed, 19 Feb 2020 13:25:52 +0200
cgrates (0.10.0) UNRELEASED; urgency=medium

View File

@@ -1164,6 +1164,7 @@ const (
MetaIPNet = "*ipnet"
MetaAPIBan = "*apiban"
MetaActivationInterval = "*ai"
MetaRegex = "*regex"
MetaNotString = "*notstring"
MetaNotPrefix = "*notprefix"
@@ -1179,6 +1180,7 @@ const (
MetaNotIPNet = "*notipnet"
MetaNotAPIBan = "*notapiban"
MetaNotActivationInterval = "*notai"
MetaNotRegex = "*notregex"
MetaEC = "*ec"
)