mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
Radius.radPassesFieldFilter with tests
This commit is contained in:
@@ -19,23 +19,37 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
package agents
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
"github.com/cgrates/radigo"
|
||||
)
|
||||
|
||||
/*
|
||||
Various RADIUS helpers here
|
||||
*/
|
||||
|
||||
func radPassesFieldFilter(pkt *radigo.Packet, fieldFilter *utils.RSRField, processorVars map[string]string) (pass bool) {
|
||||
if fieldFilter == nil {
|
||||
return true
|
||||
}
|
||||
if val, hasIt := processorVars[fieldFilter.Id]; hasIt { // ProcessorVars have priority
|
||||
if fieldFilter.FilterPasses(val) {
|
||||
return true
|
||||
pass = true
|
||||
}
|
||||
return false
|
||||
return
|
||||
}
|
||||
return
|
||||
splt := strings.Split(fieldFilter.Id, "/")
|
||||
var attrName, vendorName string
|
||||
if len(splt) > 1 {
|
||||
vendorName, attrName = splt[0], splt[1]
|
||||
} else {
|
||||
attrName = splt[0]
|
||||
}
|
||||
avps := pkt.AttributesWithName(attrName, vendorName)
|
||||
if len(avps) == 0 { // no attribute found, filter not passing
|
||||
return
|
||||
}
|
||||
for _, avp := range avps { // they all need to match the filter
|
||||
if !fieldFilter.FilterPasses(avp.StringValue()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -17,3 +17,90 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
package agents
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
"github.com/cgrates/radigo"
|
||||
)
|
||||
|
||||
var (
|
||||
dictRad *radigo.Dictionary
|
||||
coder radigo.Coder
|
||||
)
|
||||
|
||||
var freeRADIUSDocDictSample = `
|
||||
# Most of the lines are copied from freeradius documentation here:
|
||||
# http://networkradius.com/doc/3.0.10/concepts/dictionary/introduction.html
|
||||
|
||||
# Attributes
|
||||
ATTRIBUTE User-Name 1 string
|
||||
ATTRIBUTE Password 2 string
|
||||
|
||||
# Alias values
|
||||
VALUE Framed-Protocol PPP 1
|
||||
|
||||
# Vendors
|
||||
VENDOR Cisco 9
|
||||
VENDOR Microsoft 311
|
||||
|
||||
# Vendor AVPs
|
||||
BEGIN-VENDOR Cisco
|
||||
ATTRIBUTE Cisco-AVPair 1 string
|
||||
ATTRIBUTE Cisco-NAS-Port 2 string
|
||||
END-VENDOR Cisco
|
||||
`
|
||||
|
||||
func init() {
|
||||
dictRad = radigo.RFC2865Dictionary()
|
||||
// Load some VSA for our tests
|
||||
dictRad.ParseFromReader(strings.NewReader(freeRADIUSDocDictSample))
|
||||
coder = radigo.NewCoder()
|
||||
}
|
||||
|
||||
func TestRadPassesFieldFilter(t *testing.T) {
|
||||
pkt := radigo.NewPacket(radigo.AccountingRequest, 1, dictRad, coder, "CGRateS.org")
|
||||
if err := pkt.AddAVPWithName("User-Name", "flopsy", ""); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if err := pkt.AddAVPWithName("Cisco-NAS-Port", "CGR1", "Cisco"); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
//ftr :=
|
||||
if !radPassesFieldFilter(pkt, nil, nil) {
|
||||
t.Error("not passing empty filter")
|
||||
}
|
||||
if !radPassesFieldFilter(pkt,
|
||||
utils.NewRSRFieldMustCompile("User-Name(flopsy)"), nil) {
|
||||
t.Error("not passing valid filter")
|
||||
}
|
||||
if radPassesFieldFilter(pkt,
|
||||
utils.NewRSRFieldMustCompile("User-Name(notmatching)"), nil) {
|
||||
t.Error("passing invalid filter value")
|
||||
}
|
||||
if !radPassesFieldFilter(pkt,
|
||||
utils.NewRSRFieldMustCompile("Cisco/Cisco-NAS-Port(CGR1)"), nil) {
|
||||
t.Error("not passing valid filter")
|
||||
}
|
||||
if radPassesFieldFilter(pkt,
|
||||
utils.NewRSRFieldMustCompile("Cisco/Cisco-NAS-Port(notmatching)"), nil) {
|
||||
t.Error("passing invalid filter value")
|
||||
}
|
||||
if !radPassesFieldFilter(pkt,
|
||||
utils.NewRSRFieldMustCompile(fmt.Sprintf("%s(4)", MetaRadReqCode)),
|
||||
map[string]string{MetaRadReqCode: "4"}) {
|
||||
t.Error("not passing valid filter")
|
||||
}
|
||||
if radPassesFieldFilter(pkt,
|
||||
utils.NewRSRFieldMustCompile(fmt.Sprintf("%s(4)", MetaRadReqCode)),
|
||||
map[string]string{MetaRadReqCode: "5"}) {
|
||||
t.Error("passing invalid filter")
|
||||
}
|
||||
if radPassesFieldFilter(pkt,
|
||||
utils.NewRSRFieldMustCompile("UnknownField(notmatching)"), nil) {
|
||||
t.Error("passing invalid filter value")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,11 @@ import (
|
||||
"github.com/cgrates/rpcclient"
|
||||
)
|
||||
|
||||
const (
|
||||
MetaRadReqCode = "*req_code"
|
||||
MetaRadReplyCode = "*reply_code"
|
||||
)
|
||||
|
||||
func NewRadiusAgent(cgrCfg *config.CGRConfig, smg rpcclient.RpcClientConnection) (ra *RadiusAgent, err error) {
|
||||
dicts := make(map[string]*radigo.Dictionary, len(cgrCfg.RadiusAgentCfg().ClientDictionaries))
|
||||
for clntID, dictPath := range cgrCfg.RadiusAgentCfg().ClientDictionaries {
|
||||
@@ -49,29 +54,71 @@ type RadiusAgent struct {
|
||||
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
|
||||
utils.Logger.Debug(fmt.Sprintf("RadiusAgent handleAuth, received request: %+v", req))
|
||||
procVars := map[string]string{
|
||||
MetaRadReqCode: "4",
|
||||
}
|
||||
rpl = req.Reply()
|
||||
rpl.Code = radigo.AccessAccept
|
||||
for _, avp := range req.AVPs {
|
||||
rpl.AVPs = append(rpl.AVPs, avp)
|
||||
var processed bool
|
||||
for _, reqProcessor := range ra.cgrCfg.RadiusAgentCfg().RequestProcessors {
|
||||
var lclProcessed bool
|
||||
if lclProcessed, err = ra.processRequest(reqProcessor, req, procVars, rpl); lclProcessed {
|
||||
processed = lclProcessed
|
||||
}
|
||||
if err != nil || (lclProcessed && !reqProcessor.ContinueOnSuccess) {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
utils.Logger.Err(fmt.Sprintf("<RadiusAgent> request: %s, error: %s", utils.ToJSON(req), err.Error()))
|
||||
return nil, nil
|
||||
} else if !processed {
|
||||
utils.Logger.Err(fmt.Sprintf("<RadiusAgent> No request processor enabled for request: %s, ignoring request", utils.ToJSON(req)))
|
||||
return nil, nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// handleAcct handles RADIUS Accounting request
|
||||
// supports: Acct-Status-Type = Start, Interim-Update, Stop
|
||||
func (ra *RadiusAgent) handleAcct(req *radigo.Packet) (rpl *radigo.Packet, err error) {
|
||||
req.SetAVPValues()
|
||||
req.SetAVPValues() // populate string values in AVPs
|
||||
utils.Logger.Debug(fmt.Sprintf("Received request: %s", utils.ToJSON(req)))
|
||||
procVars := map[string]string{
|
||||
MetaRadReqCode: "4",
|
||||
}
|
||||
rpl = req.Reply()
|
||||
rpl.Code = radigo.AccountingResponse
|
||||
rpl.SetAVPValues()
|
||||
var processed bool
|
||||
for _, reqProcessor := range ra.cgrCfg.RadiusAgentCfg().RequestProcessors {
|
||||
var lclProcessed bool
|
||||
if lclProcessed, err = ra.processRequest(reqProcessor, req, procVars, rpl); lclProcessed {
|
||||
processed = lclProcessed
|
||||
}
|
||||
if err != nil || (lclProcessed && !reqProcessor.ContinueOnSuccess) {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
utils.Logger.Err(fmt.Sprintf("<RadiusAgent> request: %s, error: %s", utils.ToJSON(req), err.Error()))
|
||||
return nil, nil
|
||||
} else if !processed {
|
||||
utils.Logger.Err(fmt.Sprintf("<RadiusAgent> No request processor enabled for request: %s, ignoring request", utils.ToJSON(req)))
|
||||
return nil, nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (ra *RadiusAgent) processRequest(req *radigo.Packet, reqProcessor *config.RARequestProcessor) (processed bool, err error) {
|
||||
// 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) {
|
||||
passesAllFilters := true
|
||||
for _, fldFilter := range reqProcessor.RequestFilter {
|
||||
if passes := radPassesFieldFilter(req, fldFilter, nil); !passes {
|
||||
if passes := radPassesFieldFilter(req, fldFilter, processorVars); !passes {
|
||||
passesAllFilters = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,8 +81,8 @@
|
||||
{
|
||||
"id": "KamailioAccountingStart",
|
||||
"dry_run": false,
|
||||
"request_filter": "Service-Type(Sip-Session);Sip-Method(Invite)",
|
||||
"flags": [],
|
||||
"request_filter": "*radcode(4);Service-Type(Sip-Session);Sip-Method(Invite)",
|
||||
"flags": ["*radacct_start"], // flag accounting start since we have no Acct-Status-Type in request
|
||||
"continue_on_success": false,
|
||||
"append_reply": true,
|
||||
"request_fields":[
|
||||
@@ -94,8 +94,8 @@
|
||||
{
|
||||
"id": "KamailioAccountingStop",
|
||||
"dry_run": false,
|
||||
"request_filter": "Service-Type(Sip-Session);Sip-Method(Bye)",
|
||||
"flags": [],
|
||||
"request_filter": "*radcode(4)Service-Type(Sip-Session);Sip-Method(Bye)",
|
||||
"flags": ["*radacct_stop"],
|
||||
"continue_on_success": false,
|
||||
"append_reply": true,
|
||||
"request_fields":[
|
||||
|
||||
2
glide.lock
generated
2
glide.lock
generated
@@ -18,7 +18,7 @@ imports:
|
||||
- name: github.com/cgrates/osipsdagram
|
||||
version: 3d6beed663452471dec3ca194137a30d379d9e8f
|
||||
- name: github.com/cgrates/radigo
|
||||
version: 89761b5c2d44a89393c57c52b6ee472e77138bfb
|
||||
version: b6d2c57d9667095035ad4ffd0395bbbebb63ee94
|
||||
- name: github.com/cgrates/rpcclient
|
||||
version: dddae42e9344e877627cd4b7aba075d63b452c0b
|
||||
- name: github.com/ChrisTrenkamp/goxpath
|
||||
|
||||
@@ -88,6 +88,14 @@ func NewRSRField(fldStr string) (*RSRField, error) {
|
||||
return rsrField, nil
|
||||
}
|
||||
|
||||
func NewRSRFieldMustCompile(fldStr string) (rsrFld *RSRField) {
|
||||
var err error
|
||||
if rsrFld, err = NewRSRField(fldStr); err != nil {
|
||||
return nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type RSRField struct {
|
||||
Id string // Identifier
|
||||
Rules string // Rules container holding the string rules to be able to restore it after DB
|
||||
|
||||
Reference in New Issue
Block a user