Added *apiban filter

This commit is contained in:
Trial97
2020-09-25 15:06:11 +03:00
committed by Dan Christian Bogos
parent 8bb549a024
commit 0bc0b58a69
10 changed files with 254 additions and 71 deletions

View File

@@ -20,6 +20,7 @@ import (
"strings"
"time"
"github.com/cgrates/baningo"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/utils"
"github.com/cgrates/ltcache"
@@ -73,6 +74,7 @@ var (
utils.RateProfilesFilterIndexPrfx: {},
utils.RateFilterIndexPrfx: {},
utils.FilterIndexPrfx: {},
utils.MetaAPIBan: {}, // not realy a prefix as this is not stored in DB
}
)
@@ -133,7 +135,8 @@ func (dm *DataManager) CacheDataFromDB(prfx string, ids []string, mustBeCached b
if dm.cacheCfg.Partitions[utils.CachePrefixToInstance[prfx]].Limit == 0 {
return
}
if ids == nil {
if ids == nil &&
prfx != utils.MetaAPIBan { // no need for ids in this case
if mustBeCached {
ids = Cache.GetItemIDs(utils.CachePrefixToInstance[prfx], utils.EmptyString)
} else {
@@ -282,6 +285,8 @@ func (dm *DataManager) CacheDataFromDB(prfx string, ids []string, mustBeCached b
_, err = dm.GetIndexes(utils.CacheReverseFilterIndexes, dataID[:idx], dataID[idx+1:], false, true)
case utils.LoadIDPrefix:
_, err = dm.GetItemLoadIDs(utils.EmptyString, true)
case utils.MetaAPIBan:
_, err = dm.GetAPIBan(utils.EmptyString, config.CgrConfig().APIBanCfg().Keys, false, false, true)
}
if err != nil {
if err != utils.ErrNotFound {
@@ -3359,3 +3364,42 @@ func (dm *DataManager) RemoveIndexes(idxItmType, tntCtx, idxKey string) (err err
}
return
}
func (dm *DataManager) GetAPIBan(ip string, apiKeys []string, single, cacheRead, cacheWrite bool) (banned bool, err error) {
if cacheRead {
if x, ok := Cache.Get(utils.MetaAPIBan, ip); ok && x != nil { // Attempt to find in cache first
return x.(bool), nil
}
}
if single {
if banned, err = baningo.CheckIP(ip, apiKeys...); err != nil {
return
}
if cacheWrite {
if err = Cache.Set(utils.MetaAPIBan, ip, banned, nil, true, utils.NonTransactional); err != nil {
return false, err
}
}
return
}
var bannedIPs []string
if bannedIPs, err = baningo.GetBannedIPs(apiKeys...); err != nil {
return
}
for _, bannedIP := range bannedIPs {
if bannedIP == ip {
banned = true
}
if cacheWrite {
if err = Cache.Set(utils.MetaAPIBan, bannedIP, true, nil, true, utils.NonTransactional); err != nil {
return false, err
}
}
}
if len(ip) != 0 && !banned && cacheWrite {
if err = Cache.Set(utils.MetaAPIBan, ip, false, nil, true, utils.NonTransactional); err != nil {
return false, err
}
}
return
}

View File

@@ -223,16 +223,16 @@ 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.MetaNotEqual, utils.MetaIPNet, utils.MetaAPIBan})
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.MetaGreaterOrEqual, utils.MetaEqual, utils.MetaNotEqual, utils.MetaIPNet, utils.MetaAPIBan})
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.MetaEqual, utils.MetaNotEqual, utils.MetaIPNet, utils.MetaAPIBan})
// NewFilterRule returns a new filter
func NewFilterRule(rfType, fieldName string, vals []string) (*FilterRule, error) {
@@ -335,6 +335,8 @@ func (fltr *FilterRule) Pass(dDP utils.DataProvider) (result bool, err error) {
result, err = fltr.passEqualTo(dDP)
case utils.MetaIPNet, utils.MetaNotIPNet:
result, err = fltr.passIPNet(dDP)
case utils.MetaAPIBan, utils.MetaNotAPIBan:
result, err = fltr.passAPIBan(dDP)
default:
err = utils.ErrPrefixNotErrNotImplemented(fltr.Type)
}
@@ -600,6 +602,21 @@ func (fltr *FilterRule) passIPNet(dDP utils.DataProvider) (bool, error) {
return false, nil
}
func (fltr *FilterRule) passAPIBan(dDP utils.DataProvider) (bool, error) {
strVal, err := fltr.rsrElement.ParseDataProvider(dDP)
if err != nil {
if err == utils.ErrNotFound {
return false, nil
}
return false, err
}
if fltr.Values[0] != utils.MetaAll &&
fltr.Values[0] != utils.MetaSingle { // force only valid values
return false, fmt.Errorf("invalid value for apiban filter: <%s>", fltr.Values[0])
}
return dm.GetAPIBan(strVal, config.CgrConfig().APIBanCfg().Keys, fltr.Values[0] != utils.MetaAll, true, true)
}
func newDynamicDP(cfg *config.CGRConfig, connMgr *ConnManager,
tenant string, initialDP utils.DataProvider) *dynamicDP {
return &dynamicDP{

View File

@@ -15,10 +15,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package engine
import (
"net/http"
"net/http/httptest"
"reflect"
"testing"
"time"
"github.com/cgrates/baningo"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/utils"
)
@@ -1457,3 +1460,106 @@ func TestFilterPassIPNet(t *testing.T) {
t.Error(err)
}
}
func TestAPIBan(t *testing.T) {
var counter int
testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
responses := map[string]struct {
code int
body []byte
}{
"/testKey/check/1.2.3.251": {code: http.StatusOK, body: []byte(`{"ipaddress":["1.2.3.251"], "ID":"987654321"}`)},
"/testKey/check/1.2.3.254": {code: http.StatusBadRequest, body: []byte(`{"ipaddress":["not blocked"], "ID":"none"}`)},
}
if val, has := responses[r.URL.EscapedPath()]; has {
w.WriteHeader(val.code)
if val.body != nil {
w.Write(val.body)
}
return
}
counter++
w.WriteHeader(http.StatusOK)
if counter < 2 {
_, _ = w.Write([]byte(`{"ipaddress": ["1.2.3.251", "1.2.3.252"], "ID": "100"}`))
} else {
_, _ = w.Write([]byte(`{"ID": "none"}`))
counter = 0
}
}))
defer testServer.Close()
baningo.RootURL = testServer.URL + "/"
dp := utils.MapStorage{
utils.MetaReq: utils.MapStorage{
"bannedIP": "1.2.3.251",
"bannedIP2": "1.2.3.252",
"IP": "1.2.3.253",
"IP2": "1.2.3.254",
},
}
cfg, _ := config.NewDefaultCGRConfig()
data := NewInternalDB(nil, nil, true)
dmFilterPass := NewDataManager(data, config.CgrConfig().CacheCfg(), nil)
filterS := FilterS{
cfg: cfg,
dm: dmFilterPass,
}
config.CgrConfig().APIBanCfg().Keys = []string{"testKey"}
if pass, err := filterS.Pass("cgrates.org", []string{"*apiban:~*req.IP:*all"}, dp); err != nil {
t.Fatal(err)
} else if pass {
t.Error("Expected not to pass")
}
// from cache
if pass, err := filterS.Pass("cgrates.org", []string{"*apiban:~*req.IP:*all"}, dp); err != nil {
t.Fatal(err)
} else if pass {
t.Error("Expected not to pass")
}
if pass, err := filterS.Pass("cgrates.org", []string{"*apiban:~*req.IP2:*single"}, dp); err != nil {
t.Fatal(err)
} else if pass {
t.Error("Expected not to pass")
}
Cache.Clear([]string{utils.MetaAPIBan})
if pass, err := filterS.Pass("cgrates.org", []string{"*apiban:~*req.bannedIP:*single"}, dp); err != nil {
t.Fatal(err)
} else if !pass {
t.Error("Expected to pass")
}
if pass, err := filterS.Pass("cgrates.org", []string{"*apiban:~*req.bannedIP2:*all"}, dp); err != nil {
t.Fatal(err)
} else if !pass {
t.Error("Expected to pass")
}
if pass, err := filterS.Pass("cgrates.org", []string{"*apiban:~*req.notFound:*all"}, dp); err != nil {
t.Fatal(err)
} else if pass {
t.Error("Expected not to pass")
}
expErr := "invalid value for apiban filter: <*any>"
if _, err := filterS.Pass("cgrates.org", []string{"*apiban:~*req.IP:*any"}, dp); err == nil || err.Error() != expErr {
t.Errorf("Expected error %s received: %v", expErr, err)
}
baningo.RootURL = "http://127.0.0.1:80/"
expErr = `Get "http://127.0.0.1:80/testKey/banned/100": dial tcp 127.0.0.1:80: connect: connection refused`
if _, err := filterS.Pass("cgrates.org", []string{"*apiban:~*req.IP:*all"}, dp); err == nil || err.Error() != expErr {
t.Errorf("Expected error %s received: %v", expErr, err)
}
expErr = `Get "http://127.0.0.1:80/testKey/check/1.2.3.253": dial tcp 127.0.0.1:80: connect: connection refused`
if _, err := filterS.Pass("cgrates.org", []string{"*apiban:~*req.IP:*single"}, dp); err == nil || err.Error() != expErr {
t.Errorf("Expected error %s received: %v", expErr, err)
}
expErr = `invalid converter value in string: <*>, err: unsupported converter definition: <*>`
if _, err := filterS.Pass("cgrates.org", []string{"*apiban:~*req.<~*req.IP>{*}:*all"}, dp); err == nil || err.Error() != expErr {
t.Errorf("Expected error %s received: %v", expErr, err)
}
}