Dynamic templates for Radius CoA out of APIOpts

This commit is contained in:
DanB
2024-02-20 16:21:01 +01:00
parent e50d71808a
commit 556060817c
6 changed files with 82 additions and 51 deletions

View File

@@ -527,7 +527,12 @@ func (ra *RadiusAgent) V1ReAuthorize(_ *context.Context, cgrEv utils.CGREvent, r
if originID == "" {
return utils.NewErrMandatoryIeMissing(utils.OriginID)
}
replyCode, err := ra.sendRadDaReq(radigo.CoARequest, ra.cgrCfg.RadiusAgentCfg().CoATemplate,
coaTpl := ra.cgrCfg.RadiusAgentCfg().CoATemplate
if optTpl, err := cgrEv.OptAsString(utils.MetaRadCoATemplate); err == nil {
coaTpl = optTpl
}
replyCode, err := ra.sendRadDaReq(radigo.CoARequest, coaTpl,
originID, utils.MapStorage(cgrEv.Event), nil)
if err != nil {
return err

View File

@@ -132,7 +132,7 @@ func TestRadiusCoADisconnect(t *testing.T) {
if string(request.AVPs[0].RawValue) != "1001" ||
!bytes.Equal(request.AVPs[1].RawValue, decodedNasIPAddr) ||
string(request.AVPs[2].RawValue) != "e4921177ab0e3586c37f6a185864b71a@0:0:0:0:0:0:0:0" ||
string(request.AVPs[3].RawValue) != "custom_filter" {
string(request.AVPs[3].RawValue) != "mycustomvalue" {
t.Errorf("unexpected request received: %v", utils.ToJSON(request))
reply.Code = radigo.CoANAK
} else {
@@ -233,11 +233,16 @@ func TestRadiusCoADisconnect(t *testing.T) {
}
var reply string
if err := raDiscRPC.Call(context.Background(), utils.SessionSv1ReAuthorize, utils.SessionFilterWithEvent{
Event: map[string]any{
"CustomFilter": "custom_filter",
},
}, &reply); err != nil {
if err := raDiscRPC.Call(context.Background(), utils.SessionSv1ReAuthorize,
utils.SessionFilterWithEvent{
SessionFilter: &utils.SessionFilter{
APIOpts: map[string]any{
utils.MetaRadCoATemplate: "mycoa",
}},
Event: map[string]any{
"CustomFilter": "custom_filter",
},
}, &reply); err != nil {
t.Error(err)
}

View File

@@ -1163,42 +1163,42 @@ const CGRATES_CFG_JSON = `
"templates": {
"*err": [
{"tag": "SessionId", "path": "*rep.Session-Id", "type": "*variable",
"value": "~*req.Session-Id", "mandatory": true},
{"tag": "OriginHost", "path": "*rep.Origin-Host", "type": "*variable",
"value": "~*vars.OriginHost", "mandatory": true},
{"tag": "OriginRealm", "path": "*rep.Origin-Realm", "type": "*variable",
{"tag": "SessionId", "path": "*rep.Session-Id", "type": "*variable",
"value": "~*req.Session-Id", "mandatory": true},
{"tag": "OriginHost", "path": "*rep.Origin-Host", "type": "*variable",
"value": "~*vars.OriginHost", "mandatory": true},
{"tag": "OriginRealm", "path": "*rep.Origin-Realm", "type": "*variable",
"value": "~*vars.OriginRealm", "mandatory": true},
],
"*cca": [
{"tag": "SessionId", "path": "*rep.Session-Id", "type": "*variable",
"value": "~*req.Session-Id", "mandatory": true},
{"tag": "ResultCode", "path": "*rep.Result-Code", "type": "*constant",
"value": "2001"},
{"tag": "OriginHost", "path": "*rep.Origin-Host", "type": "*variable",
"value": "~*vars.OriginHost", "mandatory": true},
{"tag": "OriginRealm", "path": "*rep.Origin-Realm", "type": "*variable",
"value": "~*vars.OriginRealm", "mandatory": true},
{"tag": "AuthApplicationId", "path": "*rep.Auth-Application-Id", "type": "*variable",
"value": "~*vars.*appid", "mandatory": true},
{"tag": "CCRequestType", "path": "*rep.CC-Request-Type", "type": "*variable",
"value": "~*req.CC-Request-Type", "mandatory": true},
{"tag": "CCRequestNumber", "path": "*rep.CC-Request-Number", "type": "*variable",
"value": "~*req.CC-Request-Number", "mandatory": true},
{"tag": "SessionId", "path": "*rep.Session-Id", "type": "*variable",
"value": "~*req.Session-Id", "mandatory": true},
{"tag": "ResultCode", "path": "*rep.Result-Code", "type": "*constant",
"value": "2001"},
{"tag": "OriginHost", "path": "*rep.Origin-Host", "type": "*variable",
"value": "~*vars.OriginHost", "mandatory": true},
{"tag": "OriginRealm", "path": "*rep.Origin-Realm", "type": "*variable",
"value": "~*vars.OriginRealm", "mandatory": true},
{"tag": "AuthApplicationId", "path": "*rep.Auth-Application-Id", "type": "*variable",
"value": "~*vars.*appid", "mandatory": true},
{"tag": "CCRequestType", "path": "*rep.CC-Request-Type", "type": "*variable",
"value": "~*req.CC-Request-Type", "mandatory": true},
{"tag": "CCRequestNumber", "path": "*rep.CC-Request-Number", "type": "*variable",
"value": "~*req.CC-Request-Number", "mandatory": true},
],
"*asr": [
{"tag": "SessionId", "path": "*diamreq.Session-Id", "type": "*variable",
"value": "~*req.Session-Id", "mandatory": true},
{"tag": "OriginHost", "path": "*diamreq.Origin-Host", "type": "*variable",
"value": "~*req.Destination-Host", "mandatory": true},
{"tag": "OriginRealm", "path": "*diamreq.Origin-Realm", "type": "*variable",
"value": "~*req.Destination-Realm", "mandatory": true},
{"tag": "DestinationRealm", "path": "*diamreq.Destination-Realm", "type": "*variable",
"value": "~*req.Origin-Realm", "mandatory": true},
{"tag": "DestinationHost", "path": "*diamreq.Destination-Host", "type": "*variable",
"value": "~*req.Origin-Host", "mandatory": true},
{"tag": "AuthApplicationId", "path": "*diamreq.Auth-Application-Id", "type": "*variable",
"value": "~*vars.*appid", "mandatory": true},
{"tag": "SessionId", "path": "*diamreq.Session-Id", "type": "*variable",
"value": "~*req.Session-Id", "mandatory": true},
{"tag": "OriginHost", "path": "*diamreq.Origin-Host", "type": "*variable",
"value": "~*req.Destination-Host", "mandatory": true},
{"tag": "OriginRealm", "path": "*diamreq.Origin-Realm", "type": "*variable",
"value": "~*req.Destination-Realm", "mandatory": true},
{"tag": "DestinationRealm", "path": "*diamreq.Destination-Realm", "type": "*variable",
"value": "~*req.Origin-Realm", "mandatory": true},
{"tag": "DestinationHost", "path": "*diamreq.Destination-Host", "type": "*variable",
"value": "~*req.Origin-Host", "mandatory": true},
{"tag": "AuthApplicationId", "path": "*diamreq.Auth-Application-Id", "type": "*variable",
"value": "~*vars.*appid", "mandatory": true},
],
"*rar": [
{"tag": "SessionId", "path": "*diamreq.Session-Id", "type": "*variable",
@@ -1216,7 +1216,7 @@ const CGRATES_CFG_JSON = `
{"tag": "ReAuthRequestType", "path": "*diamreq.Re-Auth-Request-Type", "type": "*constant",
"value": "0"},
],
"*dmr": [
"*dmr": [ // used by RadiusAgent when sending Disconnect message towards the client
{"tag": "User-Name", "path": "*radDAReq.User-Name", "type": "*variable",
"value": "~*oreq.User-Name"},
{"tag": "NAS-IP-Address", "path": "*radDAReq.NAS-IP-Address", "type": "*variable",
@@ -1226,7 +1226,7 @@ const CGRATES_CFG_JSON = `
{"tag": "Reply-Message", "path": "*radDAReq.Reply-Message", "type": "*variable",
"value": "~*vars.DisconnectCause"},
],
"*coa": [
"*coa": [ // used by RadiusAgent when sending ChangeOfAuthorization message towards the client
{"tag": "User-Name", "path": "*radDAReq.User-Name", "type": "*variable",
"value": "~*oreq.User-Name"},
{"tag": "NAS-IP-Address", "path": "*radDAReq.NAS-IP-Address", "type": "*variable",
@@ -1237,10 +1237,10 @@ const CGRATES_CFG_JSON = `
"value": "~*req.CustomFilter"},
],
"*errSip": [
{"tag": "Request", "path": "*rep.Request", "type": "*constant",
"value": "SIP/2.0 500 Internal Server Error", "mandatory": true},
{"tag": "Request", "path": "*rep.Request", "type": "*constant",
"value": "SIP/2.0 500 Internal Server Error", "mandatory": true},
],
"*cdrLog": [ // cdrLog template is used in ActionS to build the event that is send to CDRs in case of *cdrLog actionType
"*cdrLog": [ // cdrLog template is used in ActionS to build the event that is sent to CDRs in case of *cdrLog actionType
{"tag": "ToR", "path": "*cdr.ToR", "type": "*variable",
"value": "~*req.BalanceType", "mandatory": true},
{"tag": "OriginHost", "path": "*cdr.OriginHost", "type": "*constant",

View File

@@ -0,0 +1,19 @@
{
"templates": {
"mycoa": [ // used by RadiusAgent when sending ChangeOfAuthorization message towards the client
{"tag": "User-Name", "path": "*radDAReq.User-Name", "type": "*variable",
"value": "~*oreq.User-Name"},
{"tag": "NAS-IP-Address", "path": "*radDAReq.NAS-IP-Address", "type": "*variable",
"value": "~*oreq.NAS-IP-Address"},
{"tag": "Acct-Session-Id", "path": "*radDAReq.Acct-Session-Id", "type": "*variable",
"value": "~*oreq.Acct-Session-Id"},
{"tag": "Filter-Id", "path": "*radDAReq.Filter-Id", "type": "*variable",
"value": "mycustomvalue"},
]
}
}

View File

@@ -4002,7 +4002,7 @@ func (sS *SessionS) BiRPCV1ProcessCDR(ctx *context.Context,
rply)
}
func (sS *SessionS) sendRar(ctx *context.Context, s *Session, event map[string]any) (err error) {
func (sS *SessionS) sendRar(ctx *context.Context, s *Session, apiOpts map[string]any, event map[string]any) (err error) {
clnt := sS.biJClnt(s.ClientConnID)
if clnt == nil {
return fmt.Errorf("calling %s requires bidirectional JSON connection, connID: <%s>",
@@ -4020,9 +4020,10 @@ func (sS *SessionS) sendRar(ctx *context.Context, s *Session, event map[string]a
}
}
args := utils.CGREvent{
ID: utils.GenUUID(),
Time: utils.TimePointer(time.Now()),
Event: event,
ID: utils.GenUUID(),
Time: utils.TimePointer(time.Now()),
APIOpts: apiOpts,
Event: event,
}
var rply string
@@ -4042,17 +4043,17 @@ func (sS *SessionS) BiRPCv1ReAuthorize(ctx *context.Context,
if len(aSs) == 0 {
return utils.ErrNotFound
}
cache := utils.NewStringSet(nil)
uniqueSIDs := utils.NewStringSet(nil)
for _, as := range aSs {
if cache.Has(as.CGRID) {
if uniqueSIDs.Has(as.CGRID) {
continue
}
cache.Add(as.CGRID)
uniqueSIDs.Add(as.CGRID)
ss := sS.getSessions(as.CGRID, false)
if len(ss) == 0 {
continue
}
if errTerm := sS.sendRar(ctx, ss[0], args.Event); errTerm != nil {
if errTerm := sS.sendRar(ctx, ss[0], args.APIOpts, args.Event); errTerm != nil {
utils.Logger.Warning(
fmt.Sprintf(
"<%s> failed sending RAR for session with id: <%s>, err: <%s>",

View File

@@ -797,6 +797,7 @@ const (
TmpSuffix = ".tmp"
MetaDiamreq = "*diamreq"
MetaRadDAReq = "*radDAReq"
MetaRadCoATemplate = "*radCoATemplate"
MetaCost = "*cost"
MetaGroup = "*group"
InternalRPCSet = "InternalRPCSet"