RadiusAgent with CGRReply in templates

This commit is contained in:
DanB
2018-02-02 16:03:48 +01:00
parent 1f7bfa00cc
commit a3e14effc3
7 changed files with 286 additions and 123 deletions

View File

@@ -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
}

View File

@@ -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())
}
}
*/

View File

@@ -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(
"<RadiusAgent> 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("<RadiusAgent> 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("<RadiusAgent> 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("<RadiusAgent> 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("<RadiusAgent> 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("<RadiusAgent> DRY_RUN, RADIUS request: %s", utils.ToJSON(req)))
utils.Logger.Info(fmt.Sprintf("<RadiusAgent> 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("<RadiusAgent> 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 {

View File

@@ -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},

View File

@@ -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
}

View File

@@ -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 {

View File

@@ -504,6 +504,10 @@ const (
FreeSWITCHAgent = "FreeSWITCHAgent"
MetaDefault = "*default"
KamailioAgent = "KamailioAgent"
RadiusAgent = "RadiusAgent"
DiameterAgent = "DiameterAgent"
Error = "Error"
MetaCGRReply = "*cgrReply"
)
//MetaMetrics