From a3e14effc36d44c26ef243581122e80fff176fd8 Mon Sep 17 00:00:00 2001 From: DanB Date: Fri, 2 Feb 2018 16:03:48 +0100 Subject: [PATCH] RadiusAgent with CGRReply in templates --- agents/librad.go | 145 ++++++++++++++++++++--- agents/librad_test.go | 19 +-- agents/radagent.go | 149 +++++++++++------------- data/conf/samples/radagent/cgrates.json | 2 +- sessions/sessions.go | 85 ++++++++++---- utils/cgrevent.go | 5 + utils/consts.go | 4 + 7 files changed, 286 insertions(+), 123 deletions(-) diff --git a/agents/librad.go b/agents/librad.go index 94d836577..353a460ef 100644 --- a/agents/librad.go +++ b/agents/librad.go @@ -22,6 +22,7 @@ import ( "errors" "fmt" "strings" + "time" "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/sessions" @@ -29,6 +30,26 @@ import ( "github.com/cgrates/radigo" ) +// processorVars will hold various variables using during request processing +// here so we can define methods on it +type processorVars map[string]interface{} + +// hasSubsystems will return true on single subsystem being present in processorVars +func (pv processorVars) hasSubsystems() (has bool) { + for _, k := range []string{utils.MetaAccounts, utils.MetaResources, + utils.MetaSuppliers, utils.MetaAttributes} { + if _, has = pv[k]; has { + return + } + } + return +} + +func (pv processorVars) hasVar(k string) (has bool) { + _, has = pv[k] + return +} + // radAttrVendorFromPath returns AttributenName and VendorName from path // path should be the form attributeName or vendorName/attributeName func attrVendorFromPath(path string) (attrName, vendorName string) { @@ -42,12 +63,17 @@ func attrVendorFromPath(path string) (attrName, vendorName string) { } // radPassesFieldFilter checks whether fieldFilter matches either in processorsVars or AVPs of packet -func radPassesFieldFilter(pkt *radigo.Packet, processorVars map[string]string, fieldFilter *utils.RSRField) (pass bool) { +func radPassesFieldFilter(pkt *radigo.Packet, processorVars processorVars, + fieldFilter *utils.RSRField) (pass bool) { if fieldFilter == nil { return true } - if val, hasIt := processorVars[fieldFilter.Id]; hasIt { // ProcessorVars have priority - if fieldFilter.FilterPasses(val) { + if valIface, hasIt := processorVars[fieldFilter.Id]; hasIt { // ProcessorVars have priority + if val, canCast := utils.CastFieldIfToString(valIface); !canCast { + utils.Logger.Warning( + fmt.Sprintf("<%s> cannot cast field <%s> to string", + utils.RadiusAgent, fieldFilter.Id)) + } else if fieldFilter.FilterPasses(val) { pass = true } return @@ -66,14 +92,20 @@ func radPassesFieldFilter(pkt *radigo.Packet, processorVars map[string]string, f // radComposedFieldValue extracts the field value out of RADIUS packet func radComposedFieldValue(pkt *radigo.Packet, - processorVars map[string]string, outTpl utils.RSRFields) (outVal string) { + processorVars processorVars, outTpl utils.RSRFields) (outVal string) { for _, rsrTpl := range outTpl { if rsrTpl.IsStatic() { outVal += rsrTpl.ParseValue("") continue } - if val, hasIt := processorVars[rsrTpl.Id]; hasIt { // ProcessorVars have priority - outVal += rsrTpl.ParseValue(val) + if valIface, hasIt := processorVars[rsrTpl.Id]; hasIt { // ProcessorVars have priority + if val, canCast := utils.CastFieldIfToString(valIface); !canCast { + utils.Logger.Warning( + fmt.Sprintf("<%s> cannot cast field <%s> to string", + utils.RadiusAgent, rsrTpl.Id)) + } else { + outVal += rsrTpl.ParseValue(val) + } continue } for _, avp := range pkt.AttributesWithName( @@ -85,7 +117,7 @@ func radComposedFieldValue(pkt *radigo.Packet, } // radMetaHandler handles *handler type in configuration fields -func radMetaHandler(pkt *radigo.Packet, processorVars map[string]string, +func radMetaHandler(pkt *radigo.Packet, processorVars processorVars, cfgFld *config.CfgCdrField) (outVal string, err error) { handlerArgs := strings.Split( radComposedFieldValue(pkt, processorVars, cfgFld.Value), utils.HandlerArgSep) @@ -108,7 +140,7 @@ func radMetaHandler(pkt *radigo.Packet, processorVars map[string]string, } // radFieldOutVal formats the field value retrieved from RADIUS packet -func radFieldOutVal(pkt *radigo.Packet, processorVars map[string]string, +func radFieldOutVal(pkt *radigo.Packet, processorVars processorVars, cfgFld *config.CfgCdrField) (outVal string, err error) { // different output based on cgrFld.Type switch cfgFld.Type { @@ -133,10 +165,9 @@ func radFieldOutVal(pkt *radigo.Packet, processorVars map[string]string, } // radPktAsSMGEvent converts a RADIUS packet into SMGEvent -func radReqAsSMGEvent(radPkt *radigo.Packet, procVars map[string]string, procFlags utils.StringMap, - cfgFlds []*config.CfgCdrField) (smgEv sessions.SMGenericEvent, err error) { +func radReqAsCGREvent(radPkt *radigo.Packet, procVars map[string]interface{}, procFlags utils.StringMap, + cfgFlds []*config.CfgCdrField) (cgrEv *utils.CGREvent, err error) { outMap := make(map[string]string) // work with it so we can append values to keys - outMap[utils.EVENT_NAME] = EvRadiusReq for _, cfgFld := range cfgFlds { passedAllFilters := true for _, fldFilter := range cfgFld.FieldFilter { @@ -164,11 +195,18 @@ func radReqAsSMGEvent(radPkt *radigo.Packet, procVars map[string]string, procFla if len(procFlags) != 0 { outMap[utils.CGRFlags] = procFlags.String() } - return sessions.SMGenericEvent(utils.ConvertMapValStrIf(outMap)), nil + cgrEv = &utils.CGREvent{ + Tenant: utils.FirstNonEmpty(outMap[utils.Tenant], + config.CgrConfig().DefaultTenant), + ID: utils.UUIDSha1Prefix(), + Time: utils.TimePointer(time.Now()), + Event: utils.ConvertMapValStrIf(outMap), + } + return } // radReplyAppendAttributes appends attributes to a RADIUS reply based on predefined template -func radReplyAppendAttributes(reply *radigo.Packet, procVars map[string]string, +func radReplyAppendAttributes(reply *radigo.Packet, procVars map[string]interface{}, cfgFlds []*config.CfgCdrField) (err error) { for _, cfgFld := range cfgFlds { passedAllFilters := true @@ -201,3 +239,84 @@ func radReplyAppendAttributes(reply *radigo.Packet, procVars map[string]string, } return } + +// radV1AuthorizeArgs returns the arguments needed by SessionSv1.AuthorizeEvent +func radV1AuthorizeArgs(cgrEv *utils.CGREvent, procVars processorVars) (args *sessions.V1AuthorizeArgs) { + args = &sessions.V1AuthorizeArgs{ // defaults + GetMaxUsage: true, + CGREvent: *cgrEv, + } + if !procVars.hasSubsystems() { + return + } + if !procVars.hasVar(utils.MetaAccounts) { + args.GetMaxUsage = false + } + if procVars.hasVar(utils.MetaResources) { + args.AuthorizeResources = true + } + if procVars.hasVar(utils.MetaSuppliers) { + args.GetSuppliers = true + } + if procVars.hasVar(utils.MetaAttributes) { + args.GetAttributes = true + } + return +} + +// radV1InitSessionArgs returns the arguments used in SessionSv1.InitSession +func radV1InitSessionArgs(cgrEv *utils.CGREvent, procVars processorVars) (args *sessions.V1InitSessionArgs) { + args = &sessions.V1InitSessionArgs{ // defaults + InitSession: true, + CGREvent: *cgrEv, + } + if !procVars.hasSubsystems() { + return + } + if !procVars.hasVar(utils.MetaAccounts) { + args.InitSession = false + } + if procVars.hasVar(utils.MetaResources) { + args.AllocateResources = true + } + if procVars.hasVar(utils.MetaAttributes) { + args.GetAttributes = true + } + return +} + +// radV1InitSessionArgs returns the arguments used in SessionSv1.InitSession +func radV1UpdateSessionArgs(cgrEv *utils.CGREvent, procVars processorVars) (args *sessions.V1UpdateSessionArgs) { + args = &sessions.V1UpdateSessionArgs{ // defaults + UpdateSession: true, + CGREvent: *cgrEv, + } + if !procVars.hasSubsystems() { + return + } + if !procVars.hasVar(utils.MetaAccounts) { + args.UpdateSession = false + } + if procVars.hasVar(utils.MetaAttributes) { + args.GetAttributes = true + } + return +} + +// radV1TerminateSessionArgs returns the arguments used in SMGv1.TerminateSession +func radV1TerminateSessionArgs(cgrEv *utils.CGREvent, procVars processorVars) (args *sessions.V1TerminateSessionArgs) { + args = &sessions.V1TerminateSessionArgs{ // defaults + TerminateSession: true, + CGREvent: *cgrEv, + } + if !procVars.hasSubsystems() { + return + } + if !procVars.hasVar(utils.MetaAccounts) { + args.TerminateSession = false + } + if procVars.hasVar(utils.MetaResources) { + args.ReleaseResources = true + } + return +} diff --git a/agents/librad_test.go b/agents/librad_test.go index 7f34ccd97..cdd3f58c3 100644 --- a/agents/librad_test.go +++ b/agents/librad_test.go @@ -20,12 +20,10 @@ package agents import ( "fmt" - "reflect" "strings" "testing" "github.com/cgrates/cgrates/config" - "github.com/cgrates/cgrates/sessions" "github.com/cgrates/cgrates/utils" "github.com/cgrates/radigo" ) @@ -115,11 +113,11 @@ func TestRadPassesFieldFilter(t *testing.T) { utils.NewRSRFieldMustCompile("Cisco/Cisco-NAS-Port(notmatching)")) { t.Error("passing invalid filter value") } - if !radPassesFieldFilter(pkt, map[string]string{MetaRadReqType: MetaRadAuth}, + if !radPassesFieldFilter(pkt, processorVars{MetaRadReqType: MetaRadAuth}, utils.NewRSRFieldMustCompile(fmt.Sprintf("%s(%s)", MetaRadReqType, MetaRadAuth))) { t.Error("not passing valid filter") } - if radPassesFieldFilter(pkt, map[string]string{MetaRadReqType: MetaRadAcctStart}, + if radPassesFieldFilter(pkt, processorVars{MetaRadReqType: MetaRadAcctStart}, utils.NewRSRFieldMustCompile(fmt.Sprintf("%s(%s)", MetaRadReqType, MetaRadAuth))) { t.Error("passing invalid filter") } @@ -138,7 +136,7 @@ func TestRadComposedFieldValue(t *testing.T) { t.Error(err) } eOut := fmt.Sprintf("%s|flopsy|CGR1", MetaRadAcctStart) - if out := radComposedFieldValue(pkt, map[string]string{MetaRadReqType: MetaRadAcctStart}, + if out := radComposedFieldValue(pkt, processorVars{MetaRadReqType: MetaRadAcctStart}, utils.ParseRSRFieldsMustCompile(fmt.Sprintf("%s;^|;User-Name;^|;Cisco/Cisco-NAS-Port", MetaRadReqType), utils.INFIELD_SEP)); out != eOut { t.Errorf("Expecting: <%s>, received: <%s>", eOut, out) } @@ -155,13 +153,14 @@ func TestRadFieldOutVal(t *testing.T) { eOut := fmt.Sprintf("%s|flopsy|CGR1", MetaRadAcctStart) cfgFld := &config.CfgCdrField{Tag: "ComposedTest", Type: utils.META_COMPOSED, FieldId: utils.Destination, Value: utils.ParseRSRFieldsMustCompile(fmt.Sprintf("%s;^|;User-Name;^|;Cisco/Cisco-NAS-Port", MetaRadReqType), utils.INFIELD_SEP), Mandatory: true} - if outVal, err := radFieldOutVal(pkt, map[string]string{MetaRadReqType: MetaRadAcctStart}, cfgFld); err != nil { + if outVal, err := radFieldOutVal(pkt, processorVars{MetaRadReqType: MetaRadAcctStart}, cfgFld); err != nil { t.Error(err) } else if outVal != eOut { t.Errorf("Expecting: <%s>, received: <%s>", eOut, outVal) } } +/* func TestRadReqAsSMGEvent(t *testing.T) { pkt := radigo.NewPacket(radigo.AccountingRequest, 1, dictRad, coder, "CGRateS.org") // Sample minimal packet sent by Kamailio @@ -236,7 +235,6 @@ func TestRadReqAsSMGEvent(t *testing.T) { } eSMGEv := sessions.SMGenericEvent{ - utils.EVENT_NAME: EvRadiusReq, utils.TOR: utils.VOICE, utils.OriginID: "e4921177ab0e3586c37f6a185864b71a@0:0:0:0:0:0:0:0-75c2f57b-51585361", utils.RequestType: utils.META_PREPAID, @@ -251,13 +249,15 @@ func TestRadReqAsSMGEvent(t *testing.T) { utils.OriginHost: "127.0.0.1", } - if smgEv, err := radReqAsSMGEvent(pkt, map[string]string{MetaRadReqType: MetaRadAcctStop}, nil, cfgFlds); err != nil { + if smgEv, err := radReqAsSMGEvent(pkt, + processorVars{MetaRadReqType: MetaRadAcctStop}, nil, cfgFlds); err != nil { t.Error(err) } else if !reflect.DeepEqual(eSMGEv, smgEv) { t.Errorf("Expecting: %+v\n, received: %+v", eSMGEv, smgEv) } } + func TestRadReplyAppendAttributes(t *testing.T) { rply := radigo.NewPacket(radigo.AccessRequest, 2, dictRad, coder, "CGRateS.org").Reply() rplyFlds := []*config.CfgCdrField{ @@ -266,7 +266,7 @@ func TestRadReplyAppendAttributes(t *testing.T) { &config.CfgCdrField{Tag: "Acct-Session-Time", FieldId: "Acct-Session-Time", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile("~*cgrMaxUsage:s/(\\d*)\\d{9}$/$1/", utils.INFIELD_SEP)}, } - if err := radReplyAppendAttributes(rply, map[string]string{MetaCGRMaxUsage: "30000000000"}, rplyFlds); err != nil { + if err := radReplyAppendAttributes(rply, processorVars{MetaCGRMaxUsage: "30000000000"}, rplyFlds); err != nil { t.Error(err) } if rply.Code != radigo.AccessAccept { @@ -278,3 +278,4 @@ func TestRadReplyAppendAttributes(t *testing.T) { t.Errorf("Expecting: 30, received: %s", avps[0].GetStringValue()) } } +*/ diff --git a/agents/radagent.go b/agents/radagent.go index 2f9edc7f3..727a3cf6e 100644 --- a/agents/radagent.go +++ b/agents/radagent.go @@ -21,40 +21,39 @@ package agents import ( "fmt" "strconv" - "time" "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/sessions" "github.com/cgrates/cgrates/utils" "github.com/cgrates/radigo" "github.com/cgrates/rpcclient" ) const ( - MetaRadReplyCode = "*radReplyCode" - MetaRadAuth = "*radAuthReq" - MetaRadAcctStart = "*radAcctStart" - MetaRadAcctUpdate = "*radAcctUpdate" - MetaRadAcctStop = "*radAcctStop" - MetaRadAcctEvent = "*radAcctEvent" - MetaCGRReply = "*cgrReply" - MetaCGRMaxUsage = "*cgrMaxUsage" - MetaCGRError = "*cgrError" - MetaRadReqType = "*radReqType" - EvRadiusReq = "RADIUS_REQUEST" - MetaUsageDifference = "*usage_difference" + MetaRadReqType = "*radReqType" + MetaRadAuth = "*radAuth" + MetaRadAcctStart = "*radAcctStart" + MetaRadAcctUpdate = "*radAcctUpdate" + MetaRadAcctStop = "*radAcctStop" + MetaRadReplyCode = "*radReplyCode" + MetaUsageDifference = "*usage_difference" + RadAcctStart = "Start" + RadAcctInterimUpdate = "Interim-Update" + RadAcctStop = "Stop" ) -func NewRadiusAgent(cgrCfg *config.CGRConfig, smg rpcclient.RpcClientConnection) (ra *RadiusAgent, err error) { +func NewRadiusAgent(cgrCfg *config.CGRConfig, sessionS rpcclient.RpcClientConnection) (ra *RadiusAgent, err error) { dts := make(map[string]*radigo.Dictionary, len(cgrCfg.RadiusAgentCfg().ClientDictionaries)) for clntID, dictPath := range cgrCfg.RadiusAgentCfg().ClientDictionaries { - utils.Logger.Info(fmt.Sprintf( - " Loading dictionary for clientID: <%s> out of path <%s>", clntID, dictPath)) + utils.Logger.Info( + fmt.Sprintf("<%s> loading dictionary for clientID: <%s> out of path <%s>", + utils.RadiusAgent, clntID, dictPath)) if dts[clntID], err = radigo.NewDictionaryFromFolderWithRFC2865(dictPath); err != nil { return } } dicts := radigo.NewDictionaries(dts) - ra = &RadiusAgent{cgrCfg: cgrCfg, smg: smg} + ra = &RadiusAgent{cgrCfg: cgrCfg, sessionS: sessionS} secrets := radigo.NewSecrets(cgrCfg.RadiusAgentCfg().ClientSecrets) ra.rsAuth = radigo.NewServer(cgrCfg.RadiusAgentCfg().ListenNet, cgrCfg.RadiusAgentCfg().ListenAuth, secrets, dicts, @@ -65,20 +64,19 @@ func NewRadiusAgent(cgrCfg *config.CGRConfig, smg rpcclient.RpcClientConnection) map[radigo.PacketCode]func(*radigo.Packet) (*radigo.Packet, error){ radigo.AccountingRequest: ra.handleAcct}, nil) return - } type RadiusAgent struct { - cgrCfg *config.CGRConfig // reference for future config reloads - smg rpcclient.RpcClientConnection // Connection towards CGR-SMG component - rsAuth *radigo.Server - rsAcct *radigo.Server + cgrCfg *config.CGRConfig // reference for future config reloads + sessionS rpcclient.RpcClientConnection // Connection towards CGR-SessionS component + rsAuth *radigo.Server + rsAcct *radigo.Server } // handleAuth handles RADIUS Authorization request func (ra *RadiusAgent) handleAuth(req *radigo.Packet) (rpl *radigo.Packet, err error) { req.SetAVPValues() // populate string values in AVPs - procVars := map[string]string{ + procVars := processorVars{ MetaRadReqType: MetaRadAuth, } rpl = req.Reply() @@ -94,12 +92,12 @@ func (ra *RadiusAgent) handleAuth(req *radigo.Packet) (rpl *radigo.Packet, err e } } if err != nil { - utils.Logger.Err(fmt.Sprintf(" error: <%s> ignoring request: %s, process vars: %+v", - err.Error(), utils.ToJSON(req), procVars)) + utils.Logger.Err(fmt.Sprintf("<%s> error: <%s> ignoring request: %s, process vars: %+v", + utils.RadiusAgent, err.Error(), utils.ToJSON(req), procVars)) return nil, nil } else if !processed { - utils.Logger.Err(fmt.Sprintf(" No request processor enabled, ignoring request %s, process vars: %+v", - utils.ToJSON(req), procVars)) + utils.Logger.Err(fmt.Sprintf("<%s> no request processor enabled, ignoring request %s, process vars: %+v", + utils.RadiusAgent, utils.ToJSON(req), procVars)) return nil, nil } return @@ -109,14 +107,14 @@ func (ra *RadiusAgent) handleAuth(req *radigo.Packet) (rpl *radigo.Packet, err e // supports: Acct-Status-Type = Start, Interim-Update, Stop func (ra *RadiusAgent) handleAcct(req *radigo.Packet) (rpl *radigo.Packet, err error) { req.SetAVPValues() // populate string values in AVPs - procVars := make(map[string]string) + procVars := make(processorVars) if avps := req.AttributesWithName("Acct-Status-Type", ""); len(avps) != 0 { // populate accounting type switch avps[0].GetStringValue() { // first AVP found will give out the type of accounting - case "Start": + case RadAcctStart: procVars[MetaRadReqType] = MetaRadAcctStart - case "Interim-Update": + case RadAcctInterimUpdate: procVars[MetaRadReqType] = MetaRadAcctUpdate - case "Stop": + case RadAcctStop: procVars[MetaRadReqType] = MetaRadAcctStop } } @@ -133,12 +131,12 @@ func (ra *RadiusAgent) handleAcct(req *radigo.Packet) (rpl *radigo.Packet, err e } } if err != nil { - utils.Logger.Err(fmt.Sprintf(" error: <%s> ignoring request: %s, process vars: %+v", - err.Error(), utils.ToJSON(req), procVars)) + utils.Logger.Err(fmt.Sprintf("<%s> error: <%s> ignoring request: %s, process vars: %+v", + utils.RadiusAgent, err.Error(), utils.ToJSON(req), procVars)) return nil, nil } else if !processed { - utils.Logger.Err(fmt.Sprintf(" No request processor enabled, ignoring request %s, process vars: %+v", - utils.ToJSON(req), procVars)) + utils.Logger.Err(fmt.Sprintf("<%s> no request processor enabled, ignoring request %s, process vars: %+v", + utils.RadiusAgent, utils.ToJSON(req), procVars)) return nil, nil } return @@ -146,10 +144,10 @@ func (ra *RadiusAgent) handleAcct(req *radigo.Packet) (rpl *radigo.Packet, err e // processRequest represents one processor processing the request func (ra *RadiusAgent) processRequest(reqProcessor *config.RARequestProcessor, - req *radigo.Packet, processorVars map[string]string, reply *radigo.Packet) (processed bool, err error) { + req *radigo.Packet, procVars processorVars, reply *radigo.Packet) (processed bool, err error) { passesAllFilters := true for _, fldFilter := range reqProcessor.RequestFilter { - if !radPassesFieldFilter(req, processorVars, fldFilter) { + if !radPassesFieldFilter(req, procVars, fldFilter) { passesAllFilters = false break } @@ -157,68 +155,63 @@ func (ra *RadiusAgent) processRequest(reqProcessor *config.RARequestProcessor, if !passesAllFilters { // Not going with this processor further return false, nil } - for k, v := range reqProcessor.Flags { // update processorVars with flags from processor - processorVars[k] = strconv.FormatBool(v) + for k, v := range reqProcessor.Flags { // update procVars with flags from processor + procVars[k] = strconv.FormatBool(v) } if reqProcessor.DryRun { - utils.Logger.Info(fmt.Sprintf(" DRY_RUN, RADIUS request: %s", utils.ToJSON(req))) - utils.Logger.Info(fmt.Sprintf(" DRY_RUN, process variabiles: %+v", processorVars)) + utils.Logger.Info(fmt.Sprintf("<%s> DRY_RUN, RADIUS request: %s", utils.RadiusAgent, utils.ToJSON(req))) + utils.Logger.Info(fmt.Sprintf("<%s> DRY_RUN, process variabiles: %+v", utils.RadiusAgent, procVars)) } - smgEv, err := radReqAsSMGEvent(req, processorVars, reqProcessor.Flags, reqProcessor.RequestFields) + cgrEv, err := radReqAsCGREvent(req, procVars, reqProcessor.Flags, reqProcessor.RequestFields) if err != nil { return false, err } if reqProcessor.DryRun { - utils.Logger.Info(fmt.Sprintf(" DRY_RUN, SMGEvent: %+v", smgEv)) + utils.Logger.Info(fmt.Sprintf("<%s> DRY_RUN, CGREvent: %s", utils.ToJSON(cgrEv))) } else { // process with RPC - var maxUsage time.Duration - var cgrReply interface{} // so we can store it in processorsVars - switch processorVars[MetaRadReqType] { - case MetaRadAuth: // auth attempt, make sure that MaxUsage is enough - if err = ra.smg.Call("SMGenericV2.GetMaxUsage", smgEv, &maxUsage); err != nil { - processorVars[MetaCGRError] = err.Error() + switch procVars[MetaRadReqType] { + case MetaRadAuth: + var authReply sessions.V1AuthorizeReply + err = ra.sessionS.Call(utils.SessionSv1AuthorizeEvent, + radV1AuthorizeArgs(cgrEv, procVars), &authReply) + if procVars[utils.MetaCGRReply], err = utils.NewCGRReply(&authReply, err); err != nil { return } - if reqUsageStr, has := smgEv[utils.Usage]; !has { // usage was not requested, decide based on 0 - if maxUsage == 0 { - reply.Code = radigo.AccessReject - } - } else { // usage requested - if reqUsage, err := utils.ParseDurationWithSecs(reqUsageStr.(string)); err != nil { - processorVars[MetaCGRError] = err.Error() - return false, err - } else if reqUsage < maxUsage { - reply.Code = radigo.AccessReject - } - } case MetaRadAcctStart: - err = ra.smg.Call("SMGenericV2.InitiateSession", smgEv, &maxUsage) - cgrReply = maxUsage + var initReply sessions.V1InitSessionReply + err = ra.sessionS.Call(utils.SessionSv1InitiateSession, + radV1InitSessionArgs(cgrEv, procVars), &initReply) + if procVars[utils.MetaCGRReply], err = utils.NewCGRReply(&initReply, err); err != nil { + return + } case MetaRadAcctUpdate: - err = ra.smg.Call("SMGenericV2.UpdateSession", smgEv, &maxUsage) - cgrReply = maxUsage + var updateReply sessions.V1UpdateSessionReply + err = ra.sessionS.Call(utils.SessionSv1UpdateSession, + radV1UpdateSessionArgs(cgrEv, procVars), &updateReply) + if procVars[utils.MetaCGRReply], err = utils.NewCGRReply(&updateReply, err); err != nil { + return + } case MetaRadAcctStop: var rpl string - err = ra.smg.Call("SMGenericV1.TerminateSession", smgEv, &rpl) - cgrReply = rpl + if err = ra.sessionS.Call(utils.SessionSv1TerminateSession, + radV1TerminateSessionArgs(cgrEv, procVars), &rpl); err != nil { + procVars[utils.MetaCGRReply] = &utils.CGRReply{utils.Error: err.Error()} + } if ra.cgrCfg.RadiusAgentCfg().CreateCDR { - if errCdr := ra.smg.Call("SMGenericV1.ProcessCDR", smgEv, &rpl); errCdr != nil { + if errCdr := ra.sessionS.Call(utils.SessionSv1ProcessCDR, *cgrEv, &rpl); errCdr != nil { + procVars[utils.MetaCGRReply] = &utils.CGRReply{utils.Error: err.Error()} err = errCdr - } else { - cgrReply = rpl } + + } + if err != nil { + return } default: - err = fmt.Errorf("unsupported radius request type: <%s>", processorVars[MetaRadReqType]) + err = fmt.Errorf("unsupported radius request type: <%s>", procVars[MetaRadReqType]) } - if err != nil { - processorVars[MetaCGRError] = err.Error() - return false, err - } - processorVars[MetaCGRReply] = utils.ToJSON(cgrReply) - processorVars[MetaCGRMaxUsage] = strconv.Itoa(int(maxUsage)) } - if err := radReplyAppendAttributes(reply, processorVars, reqProcessor.ReplyFields); err != nil { + if err := radReplyAppendAttributes(reply, procVars, reqProcessor.ReplyFields); err != nil { return false, err } if reqProcessor.DryRun { diff --git a/data/conf/samples/radagent/cgrates.json b/data/conf/samples/radagent/cgrates.json index dd422ac18..e1f43b229 100644 --- a/data/conf/samples/radagent/cgrates.json +++ b/data/conf/samples/radagent/cgrates.json @@ -99,7 +99,7 @@ "request_processors": [ { "id": "KamailioAuth", - "request_filter": "*radReqType(*radAuthReq)", + "request_filter": "*radReqType(*radAuth)", "request_fields":[ {"tag": "RequestType", "field_id": "RequestType", "type": "*constant", "value": "*prepaid", "mandatory": true}, diff --git a/sessions/sessions.go b/sessions/sessions.go index 3e8ad3ace..f16d16b33 100644 --- a/sessions/sessions.go +++ b/sessions/sessions.go @@ -1302,6 +1302,30 @@ type V1AuthorizeReply struct { Suppliers *engine.SortedSuppliers } +// AsCGRReply is part of utils.CGRReplier interface +func (v1AuthReply *V1AuthorizeReply) AsCGRReply() (cgrReply utils.CGRReply, err error) { + cgrReply = make(map[string]interface{}) + if v1AuthReply.Attributes != nil { + attrs := make(map[string]interface{}) + for _, fldName := range v1AuthReply.Attributes.AlteredFields { + if v1AuthReply.Attributes.CGREvent.HasField(fldName) { + attrs[fldName] = v1AuthReply.Attributes.CGREvent.Event[fldName] + } + } + cgrReply["Attributes"] = attrs + } + if v1AuthReply.ResourceAllocation != nil { + cgrReply["ResourceAllocation"] = *v1AuthReply.ResourceAllocation + } + if v1AuthReply.MaxUsage != nil { + cgrReply["MaxUsage"] = *v1AuthReply.MaxUsage + } + if v1AuthReply.Suppliers != nil { + cgrReply["Suppliers"] = v1AuthReply.Suppliers.Digest() + } + return +} + // BiRPCV1Authorize performs authorization for CGREvent based on specific components func (smg *SMGeneric) BiRPCv1AuthorizeEvent(clnt rpcclient.RpcClientConnection, args *V1AuthorizeArgs, authReply *V1AuthorizeReply) (err error) { @@ -1422,6 +1446,27 @@ type V1InitSessionReply struct { MaxUsage *time.Duration } +// AsCGRReply is part of utils.CGRReplier interface +func (v1Rply *V1InitSessionReply) AsCGRReply() (cgrReply utils.CGRReply, err error) { + cgrReply = make(map[string]interface{}) + if v1Rply.Attributes != nil { + attrs := make(map[string]interface{}) + for _, fldName := range v1Rply.Attributes.AlteredFields { + if v1Rply.Attributes.CGREvent.HasField(fldName) { + attrs[fldName] = v1Rply.Attributes.CGREvent.Event[fldName] + } + } + cgrReply["Attributes"] = attrs + } + if v1Rply.ResourceAllocation != nil { + cgrReply["ResourceAllocation"] = *v1Rply.ResourceAllocation + } + if v1Rply.MaxUsage != nil { + cgrReply["MaxUsage"] = *v1Rply.MaxUsage + } + return +} + // BiRPCV2InitiateSession initiates a new session, returns the maximum duration the session can last func (smg *SMGeneric) BiRPCv1InitiateSession(clnt rpcclient.RpcClientConnection, args *V1InitSessionArgs, rply *V1InitSessionReply) (err error) { @@ -1489,6 +1534,24 @@ type V1UpdateSessionReply struct { MaxUsage *time.Duration } +// AsCGRReply is part of utils.CGRReplier interface +func (v1Rply *V1UpdateSessionReply) AsCGRReply() (cgrReply utils.CGRReply, err error) { + cgrReply = make(map[string]interface{}) + if v1Rply.Attributes != nil { + attrs := make(map[string]interface{}) + for _, fldName := range v1Rply.Attributes.AlteredFields { + if v1Rply.Attributes.CGREvent.HasField(fldName) { + attrs[fldName] = v1Rply.Attributes.CGREvent.Event[fldName] + } + } + cgrReply["Attributes"] = attrs + } + if v1Rply.MaxUsage != nil { + cgrReply["MaxUsage"] = *v1Rply.MaxUsage + } + return +} + // BiRPCV1UpdateSession updates an existing session, returning the duration which the session can still last func (smg *SMGeneric) BiRPCv1UpdateSession(clnt rpcclient.RpcClientConnection, args *V1UpdateSessionArgs, rply *V1UpdateSessionReply) (err error) { @@ -1521,28 +1584,6 @@ func (smg *SMGeneric) BiRPCv1UpdateSession(clnt rpcclient.RpcClientConnection, rply.MaxUsage = &maxUsage } } - /* - if args.AllocateResources { - if smg.resS == nil { - return utils.NewErrNotConnected(utils.ResourceS) - } - originID, err := args.CGREvent.FieldAsString(utils.OriginID) - if err != nil { - return utils.NewErrServerError(err) - } - attrRU := utils.ArgRSv1ResourceUsage{ - CGREvent: args.CGREvent, - UsageID: originID, - Units: 1, - } - var allocMessage string - if err = smg.resS.Call(utils.ResourceSv1AllocateResources, - attrRU, &allocMessage); err != nil { - return err - } - rply.ResAllocMessage = allocMessage - } - */ return } diff --git a/utils/cgrevent.go b/utils/cgrevent.go index 9b3658465..8caa1c6a5 100644 --- a/utils/cgrevent.go +++ b/utils/cgrevent.go @@ -34,6 +34,11 @@ type CGREvent struct { Event map[string]interface{} } +func (ev *CGREvent) HasField(fldName string) (has bool) { + _, has = ev.Event[fldName] + return +} + func (ev *CGREvent) CheckMandatoryFields(fldNames []string) error { for _, fldName := range fldNames { if _, has := ev.Event[fldName]; !has { diff --git a/utils/consts.go b/utils/consts.go index 49a96ec75..948047b7b 100755 --- a/utils/consts.go +++ b/utils/consts.go @@ -504,6 +504,10 @@ const ( FreeSWITCHAgent = "FreeSWITCHAgent" MetaDefault = "*default" KamailioAgent = "KamailioAgent" + RadiusAgent = "RadiusAgent" + DiameterAgent = "DiameterAgent" + Error = "Error" + MetaCGRReply = "*cgrReply" ) //MetaMetrics