mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
Added new fields in sentrypeerCfg && implemented fallback loop on unauthorized requests
This commit is contained in:
committed by
Dan Christian Bogos
parent
9af02771fd
commit
2e93354ad1
@@ -1268,9 +1268,13 @@ const CGRATES_CFG_JSON = `
|
||||
"keys": [],
|
||||
},
|
||||
"sentrypeer":{
|
||||
"url":"",
|
||||
"clientID":"",
|
||||
"clientSecret":"",
|
||||
}
|
||||
"client_id":"",
|
||||
"client_secret":"",
|
||||
"token_url":"https://authz.sentrypeer.com/oauth/token",
|
||||
"ip_url":"https://sentrypeer.com/api/ip-addresses",
|
||||
"number_url":"https://sentrypeer.com/api/phone-numbers",
|
||||
"audience":"https://sentrypeer.com/api",
|
||||
"grant_type":"client_credentials",
|
||||
},
|
||||
|
||||
}`
|
||||
|
||||
@@ -877,9 +877,13 @@ type APIBanJsonCfg struct {
|
||||
}
|
||||
|
||||
type SentryPeerJsonCfg struct {
|
||||
ClientID *string
|
||||
ClientSecret *string
|
||||
Url *string
|
||||
ClientID *string `json:"client_id"`
|
||||
ClientSecret *string `json:"client_secret"`
|
||||
TokenUrl *string `json:"token_url"`
|
||||
IpUrl *string `json:"ip_url"`
|
||||
NumberUrl *string `json:"number_url"`
|
||||
Audience *string `json:"audience"`
|
||||
GrantType *string `json:"grant_type"`
|
||||
}
|
||||
|
||||
type CoreSJsonCfg struct {
|
||||
|
||||
@@ -21,15 +21,25 @@ package config
|
||||
type SentryPeerCfg struct {
|
||||
ClientID string
|
||||
ClientSecret string
|
||||
Url string
|
||||
TokenUrl string
|
||||
IpUrl string
|
||||
NumberUrl string
|
||||
Audience string
|
||||
GrantType string
|
||||
}
|
||||
|
||||
func (sp *SentryPeerCfg) loadFromJSONCfg(jsnCfg *SentryPeerJsonCfg) (err error) {
|
||||
if jsnCfg == nil {
|
||||
return
|
||||
}
|
||||
if jsnCfg.Url != nil {
|
||||
sp.Url = *jsnCfg.Url
|
||||
if jsnCfg.TokenUrl != nil {
|
||||
sp.TokenUrl = *jsnCfg.TokenUrl
|
||||
}
|
||||
if jsnCfg.IpUrl != nil {
|
||||
sp.IpUrl = *jsnCfg.IpUrl
|
||||
}
|
||||
if jsnCfg.NumberUrl != nil {
|
||||
sp.NumberUrl = *jsnCfg.NumberUrl
|
||||
}
|
||||
if jsnCfg.ClientSecret != nil {
|
||||
sp.ClientSecret = *jsnCfg.ClientSecret
|
||||
@@ -37,22 +47,36 @@ func (sp *SentryPeerCfg) loadFromJSONCfg(jsnCfg *SentryPeerJsonCfg) (err error)
|
||||
if jsnCfg.ClientID != nil {
|
||||
sp.ClientID = *jsnCfg.ClientID
|
||||
}
|
||||
if jsnCfg.Audience != nil {
|
||||
sp.Audience = *jsnCfg.Audience
|
||||
}
|
||||
if jsnCfg.GrantType != nil {
|
||||
sp.GrantType = *jsnCfg.GrantType
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (sp *SentryPeerCfg) AsMapInterface() map[string]any {
|
||||
return map[string]any{
|
||||
"URL": sp.Url,
|
||||
"TokenURL": sp.TokenUrl,
|
||||
"ClientSecret": sp.ClientSecret,
|
||||
"ClientID": sp.ClientID,
|
||||
"IpUrl": sp.IpUrl,
|
||||
"NumberUrl": sp.NumberUrl,
|
||||
"Audience": sp.Audience,
|
||||
"GrantType": sp.GrantType,
|
||||
}
|
||||
}
|
||||
|
||||
func (sp *SentryPeerCfg) Clone() (cln *SentryPeerCfg) {
|
||||
cln = &SentryPeerCfg{
|
||||
Url: sp.Url,
|
||||
TokenUrl: sp.TokenUrl,
|
||||
ClientSecret: sp.ClientSecret,
|
||||
ClientID: sp.ClientID,
|
||||
IpUrl: sp.IpUrl,
|
||||
NumberUrl: sp.NumberUrl,
|
||||
Audience: sp.Audience,
|
||||
GrantType: sp.GrantType,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -111,11 +111,5 @@
|
||||
"apiers_conns": ["*internal"]
|
||||
}
|
||||
|
||||
//"sentrypeer":{
|
||||
//"url":"https://sentrypeer.com/api/",
|
||||
//"clientID":"",
|
||||
//"clientSecret":"",
|
||||
//},
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -128,9 +128,13 @@
|
||||
},
|
||||
|
||||
// "sentrypeer":{
|
||||
// "url":"https://sentrypeer.com/api/",
|
||||
// "clientID":"",
|
||||
// "clientSecret":"",
|
||||
// },
|
||||
// "client_id":"",
|
||||
// "client_secret":"",
|
||||
// "token_url":"",
|
||||
// "ip_url":"",
|
||||
// "number_url":"",
|
||||
// "audience":"",
|
||||
// "grant_type":"",
|
||||
// },
|
||||
|
||||
}
|
||||
|
||||
@@ -118,10 +118,5 @@
|
||||
"apiers_conns": ["*internal"],
|
||||
},
|
||||
|
||||
//"sentrypeer":{
|
||||
//"url":"https://sentrypeer.com/api/",
|
||||
//"clientID":"",
|
||||
//"clientSecret":"",
|
||||
//},
|
||||
|
||||
}
|
||||
|
||||
@@ -270,7 +270,7 @@ func (dm *DataManager) CacheDataFromDB(prfx string, ids []string, mustBeCached b
|
||||
case utils.MetaAPIBan:
|
||||
_, err = dm.GetAPIBan(utils.EmptyString, config.CgrConfig().APIBanCfg().Keys, false, false, true)
|
||||
case utils.MetaSentryPeer:
|
||||
_, err = GetSentryPeer(utils.EmptyString, config.CgrConfig().SentryPeerCfg().Url, config.CgrConfig().SentryPeerCfg().ClientID, config.CgrConfig().SentryPeerCfg().ClientSecret, utils.EmptyString, false, true)
|
||||
_, err = GetSentryPeer(utils.EmptyString, config.CgrConfig().SentryPeerCfg(), utils.EmptyString)
|
||||
}
|
||||
if err != nil {
|
||||
if err != utils.ErrNotFound && err != utils.ErrDSPProfileNotFound && err != utils.ErrDSPHostNotFound {
|
||||
|
||||
@@ -30,10 +30,6 @@ import (
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
)
|
||||
|
||||
type TokenResponse struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
}
|
||||
|
||||
// MatchingItemIDsForEvent returns the list of item IDs matching fieldName/fieldValue for an event
|
||||
// fieldIDs limits the fields which are checked against indexes
|
||||
// helper on top of dataDB.GetIndexes, adding utils.MetaAny to list of fields queried
|
||||
@@ -127,101 +123,107 @@ func WeightFromDynamics(dWs []*utils.DynamicWeight,
|
||||
}
|
||||
return 0.0, nil
|
||||
}
|
||||
func getToken(clientID, clientSecret string, cacheWrite bool, token *TokenResponse) (err error) {
|
||||
func getToken(tokenUrl, clientID, clientSecret, audience, grantType string) (token string, err error) {
|
||||
var resp *http.Response
|
||||
payload := map[string]string{
|
||||
"client_id": clientID,
|
||||
"client_secret": clientSecret,
|
||||
"audience": "https://sentrypeer.com/api",
|
||||
"grant_type": "client_credentials",
|
||||
"audience": audience,
|
||||
"grant_type": grantType,
|
||||
}
|
||||
jsonPayload, _ := json.Marshal(payload)
|
||||
resp, err = getHTTP("POST", "https://authz.sentrypeer.com/oauth/token", bytes.NewBuffer(jsonPayload), map[string][]string{"Content-Type": {"application/json"}})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
err = json.NewDecoder(resp.Body).Decode(&token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cacheWrite {
|
||||
if err = Cache.Set(utils.MetaSentryPeer, "*token", token.AccessToken, nil, true, utils.NonTransactional); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
func GetSentryPeer(val, url, clientID, clientSecret, path string, cacheRead, cacheWrite bool) (found bool, err error) {
|
||||
valpath := utils.ConcatenatedKey(path, val)
|
||||
var (
|
||||
token TokenResponse
|
||||
resp *http.Response
|
||||
cachedReq bool
|
||||
)
|
||||
if cacheRead {
|
||||
if x, ok := Cache.Get(utils.MetaSentryPeer, valpath); ok && x != nil { // Attempt to find in cache first
|
||||
return x.(bool), nil
|
||||
}
|
||||
var cachedToken any
|
||||
if cachedToken, cachedReq = Cache.Get(utils.MetaSentryPeer, "*token"); cachedReq && cachedToken != nil {
|
||||
token.AccessToken = cachedToken.(string)
|
||||
}
|
||||
}
|
||||
if !cachedReq {
|
||||
if err = getToken(clientID, clientSecret, cacheWrite, &token); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
switch path {
|
||||
case "*ip":
|
||||
url += "ip-addresses/"
|
||||
case "*number":
|
||||
url += "phone-numbers/"
|
||||
}
|
||||
resp, err = getHTTP("GET", url+val, nil, map[string][]string{"Authorization": {fmt.Sprintf("Bearer %v", token.AccessToken)}})
|
||||
resp, err = getHTTP("POST", tokenUrl, bytes.NewBuffer(jsonPayload), map[string][]string{"Content-Type": {"application/json"}})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden {
|
||||
Cache.Remove(utils.MetaSentryPeer, "*token", true, utils.NonTransactional)
|
||||
utils.Logger.Warning("SentryPeer redirecting to new bearer token ")
|
||||
if err = getToken(clientID, clientSecret, cacheWrite, &token); err != nil {
|
||||
var m struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
}
|
||||
err = json.NewDecoder(resp.Body).Decode(&m)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
token = m.AccessToken
|
||||
return
|
||||
}
|
||||
func GetSentryPeer(val string, sentryPeerCfg *config.SentryPeerCfg, path string) (found bool, err error) {
|
||||
itemId := utils.ConcatenatedKey(path, val)
|
||||
var (
|
||||
isCached bool
|
||||
url string
|
||||
token string
|
||||
)
|
||||
if x, ok := Cache.Get(utils.MetaSentryPeer, itemId); ok && x != nil { // Attempt to find in cache first
|
||||
return x.(bool), nil
|
||||
}
|
||||
var cachedToken any
|
||||
if cachedToken, isCached = Cache.Get(utils.MetaSentryPeer,
|
||||
utils.MetaToken); isCached && cachedToken != nil {
|
||||
token = cachedToken.(string)
|
||||
}
|
||||
switch path {
|
||||
case utils.MetaIp:
|
||||
url = sentryPeerCfg.IpUrl + "/" + val
|
||||
case utils.MetaNumber:
|
||||
url = sentryPeerCfg.NumberUrl + "/" + val
|
||||
}
|
||||
if !isCached {
|
||||
if token, err = getToken(sentryPeerCfg.TokenUrl, sentryPeerCfg.ClientID, sentryPeerCfg.ClientSecret,
|
||||
sentryPeerCfg.Audience, sentryPeerCfg.GrantType); err != nil {
|
||||
return
|
||||
}
|
||||
resp, err = getHTTP("GET", url+val, nil, map[string][]string{"Authorization": {fmt.Sprintf("Bearer %v", token.AccessToken)}})
|
||||
if err != nil {
|
||||
if err = Cache.Set(utils.MetaSentryPeer, utils.MetaToken,
|
||||
token, nil, true, utils.NonTransactional); err != nil {
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden {
|
||||
return false, fmt.Errorf("still unauthorized after getting new token")
|
||||
}
|
||||
}
|
||||
switch {
|
||||
case resp.StatusCode >= http.StatusOK && resp.StatusCode < http.StatusMultipleChoices:
|
||||
if cacheWrite {
|
||||
if err = Cache.Set(utils.MetaSentryPeer, valpath, false, nil, true, utils.NonTransactional); err != nil {
|
||||
|
||||
for i := 0; i < 2; i++ {
|
||||
if found, err = hasData(itemId, token, url); err == nil {
|
||||
if err = Cache.Set(utils.MetaSentryPeer, itemId, found,
|
||||
nil, true, utils.NonTransactional); err != nil {
|
||||
return
|
||||
}
|
||||
break
|
||||
} else if err != utils.ErrNotAuthorized {
|
||||
break
|
||||
}
|
||||
Cache.Remove(utils.MetaSentryPeer, utils.MetaToken, true, utils.EmptyString)
|
||||
if token, err = getToken(sentryPeerCfg.TokenUrl, sentryPeerCfg.ClientID, sentryPeerCfg.ClientSecret,
|
||||
sentryPeerCfg.Audience, sentryPeerCfg.GrantType); err != nil {
|
||||
return
|
||||
}
|
||||
if err = Cache.Set(utils.MetaSentryPeer, utils.MetaToken, token,
|
||||
nil, true, utils.NonTransactional); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func hasData(itemId, token, url string) (found bool, err error) {
|
||||
var resp *http.Response
|
||||
resp, err = getHTTP("GET", url, nil, map[string][]string{"Authorization": {fmt.Sprintf("Bearer %v", token)}})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
switch {
|
||||
case resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden:
|
||||
return false, utils.ErrNotAuthorized
|
||||
case resp.StatusCode >= http.StatusOK && resp.StatusCode < http.StatusMultipleChoices:
|
||||
return false, nil
|
||||
case resp.StatusCode == http.StatusNotFound:
|
||||
if cacheWrite {
|
||||
if err = Cache.Set(utils.MetaSentryPeer, valpath, true, nil, true, utils.NonTransactional); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
case resp.StatusCode >= http.StatusBadRequest && resp.StatusCode < http.StatusInternalServerError:
|
||||
err = fmt.Errorf("client err <%v>", resp.Status)
|
||||
err = fmt.Errorf("sentrypeer api got client err <%v>", resp.Status)
|
||||
case resp.StatusCode >= http.StatusInternalServerError:
|
||||
err = fmt.Errorf("server error<%s>", resp.Status)
|
||||
err = fmt.Errorf("sentrypeer api got server error<%s>", resp.Status)
|
||||
default:
|
||||
err = fmt.Errorf("unexpected status code<%s>", resp.Status)
|
||||
err = fmt.Errorf("sentrypeer api got unexpected status code<%s>", resp.Status)
|
||||
}
|
||||
utils.Logger.Warning(fmt.Sprintf("Sentrypeer filter got %v ", err.Error()))
|
||||
utils.Logger.Warning(err.Error())
|
||||
return false, err
|
||||
}
|
||||
|
||||
|
||||
@@ -656,10 +656,10 @@ func (fltr *FilterRule) passSentryPeer(dDP utils.DataProvider) (bool, error) {
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
if fltr.Values[0] != "*number" && fltr.Values[0] != "*ip" {
|
||||
if fltr.Values[0] != utils.MetaNumber && fltr.Values[0] != utils.MetaIp {
|
||||
return false, fmt.Errorf("invalid value for sentrypeer filter: <%s>", fltr.Values[0])
|
||||
}
|
||||
return GetSentryPeer(strVal, config.CgrConfig().SentryPeerCfg().Url, config.CgrConfig().SentryPeerCfg().ClientID, config.CgrConfig().SentryPeerCfg().ClientSecret, fltr.Values[0], true, true)
|
||||
return GetSentryPeer(strVal, config.CgrConfig().SentryPeerCfg(), fltr.Values[0])
|
||||
}
|
||||
|
||||
func parseTime(rsr *config.RSRParser, dDp utils.DataProvider) (_ time.Time, err error) {
|
||||
|
||||
@@ -1159,6 +1159,9 @@ const (
|
||||
MetaIPNet = "*ipnet"
|
||||
MetaAPIBan = "*apiban"
|
||||
MetaSentryPeer = "*sentrypeer"
|
||||
MetaToken = "*token"
|
||||
MetaIp = "*ip"
|
||||
MetaNumber = "*number"
|
||||
MetaActivationInterval = "*ai"
|
||||
MetaRegex = "*regex"
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ var (
|
||||
RalsErrorPrfx = "RALS_ERROR"
|
||||
DispatcherErrorPrefix = "DISPATCHER_ERROR"
|
||||
RateSErrPrfx = "RATES_ERROR"
|
||||
ErrNotAuthorized = errors.New("NOT_AUTHORIZED")
|
||||
ErrUnsupportedFormat = errors.New("UNSUPPORTED_FORMAT")
|
||||
ErrNoDatabaseConn = errors.New("NO_DATABASE_CONNECTION")
|
||||
ErrMaxIncrementsExceeded = errors.New("MAX_INCREMENTS_EXCEEDED")
|
||||
|
||||
Reference in New Issue
Block a user