mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
RadiusAgent with CGRReply in templates
This commit is contained in:
145
agents/librad.go
145
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
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -504,6 +504,10 @@ const (
|
||||
FreeSWITCHAgent = "FreeSWITCHAgent"
|
||||
MetaDefault = "*default"
|
||||
KamailioAgent = "KamailioAgent"
|
||||
RadiusAgent = "RadiusAgent"
|
||||
DiameterAgent = "DiameterAgent"
|
||||
Error = "Error"
|
||||
MetaCGRReply = "*cgrReply"
|
||||
)
|
||||
|
||||
//MetaMetrics
|
||||
|
||||
Reference in New Issue
Block a user