mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-02 19:40:19 +08:00
feat: support radius accounting request (#2362)
* feat: add radius server * feat: parse org from packet * feat: add comment * feat: support radius accounting * feat: change log * feat: add copyright
This commit is contained in:
@ -15,24 +15,27 @@
|
||||
package radius
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/casdoor/casdoor/conf"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"layeh.com/radius"
|
||||
"layeh.com/radius/rfc2865"
|
||||
"layeh.com/radius/rfc2866"
|
||||
)
|
||||
|
||||
// https://support.huawei.com/enterprise/zh/doc/EDOC1000178159/35071f9a#tab_3
|
||||
func StartRadiusServer() {
|
||||
secret := conf.GetConfigString("radiusSecret")
|
||||
server := radius.PacketServer{
|
||||
Addr: "0.0.0.0:" + conf.GetConfigString("radiusServerPort"),
|
||||
Handler: radius.HandlerFunc(handlerRadius),
|
||||
SecretSource: radius.StaticSecretSource([]byte(`secret`)),
|
||||
SecretSource: radius.StaticSecretSource([]byte(secret)),
|
||||
}
|
||||
log.Printf("Starting Radius server on %s", server.Addr)
|
||||
if err := server.ListenAndServe(); err != nil {
|
||||
log.Printf("StartRadiusServer() failed, err = %s", err.Error())
|
||||
log.Printf("StartRadiusServer() failed, err = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,6 +43,8 @@ func handlerRadius(w radius.ResponseWriter, r *radius.Request) {
|
||||
switch r.Code {
|
||||
case radius.CodeAccessRequest:
|
||||
handleAccessRequest(w, r)
|
||||
case radius.CodeAccountingRequest:
|
||||
handleAccountingRequest(w, r)
|
||||
default:
|
||||
log.Printf("radius message, code = %d", r.Code)
|
||||
}
|
||||
@ -48,20 +53,57 @@ func handlerRadius(w radius.ResponseWriter, r *radius.Request) {
|
||||
func handleAccessRequest(w radius.ResponseWriter, r *radius.Request) {
|
||||
username := rfc2865.UserName_GetString(r.Packet)
|
||||
password := rfc2865.UserPassword_GetString(r.Packet)
|
||||
organization := parseOrganization(r.Packet)
|
||||
code := radius.CodeAccessAccept
|
||||
|
||||
log.Printf("username=%v, password=%v, code=%v, org=%v", username, password, code, organization)
|
||||
organization := rfc2865.Class_GetString(r.Packet)
|
||||
log.Printf("handleAccessRequest() username=%v, org=%v, password=%v", username, organization, password)
|
||||
if organization == "" {
|
||||
code = radius.CodeAccessReject
|
||||
w.Write(r.Response(code))
|
||||
w.Write(r.Response(radius.CodeAccessReject))
|
||||
return
|
||||
}
|
||||
_, msg := object.CheckUserPassword(organization, username, password, "en")
|
||||
if msg != "" {
|
||||
code = radius.CodeAccessReject
|
||||
w.Write(r.Response(code))
|
||||
w.Write(r.Response(radius.CodeAccessReject))
|
||||
return
|
||||
}
|
||||
w.Write(r.Response(code))
|
||||
w.Write(r.Response(radius.CodeAccessAccept))
|
||||
}
|
||||
|
||||
func handleAccountingRequest(w radius.ResponseWriter, r *radius.Request) {
|
||||
statusType := rfc2866.AcctStatusType_Get(r.Packet)
|
||||
username := rfc2865.UserName_GetString(r.Packet)
|
||||
organization := rfc2865.Class_GetString(r.Packet)
|
||||
log.Printf("handleAccountingRequest() username=%v, org=%v, statusType=%v", username, organization, statusType)
|
||||
w.Write(r.Response(radius.CodeAccountingResponse))
|
||||
var err error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.Printf("handleAccountingRequest() failed, err = %v", err)
|
||||
}
|
||||
}()
|
||||
switch statusType {
|
||||
case rfc2866.AcctStatusType_Value_Start:
|
||||
// Start an accounting session
|
||||
ra := GetAccountingFromRequest(r)
|
||||
err = object.AddRadiusAccounting(ra)
|
||||
case rfc2866.AcctStatusType_Value_InterimUpdate, rfc2866.AcctStatusType_Value_Stop:
|
||||
// Interim update to an accounting session | Stop an accounting session
|
||||
var (
|
||||
newRa = GetAccountingFromRequest(r)
|
||||
oldRa *object.RadiusAccounting
|
||||
)
|
||||
oldRa, err = object.GetRadiusAccountingBySessionId(newRa.AcctSessionId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if oldRa == nil {
|
||||
if err = object.AddRadiusAccounting(newRa); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
stop := statusType == rfc2866.AcctStatusType_Value_Stop
|
||||
err = object.InterimUpdateRadiusAccounting(oldRa, newRa, stop)
|
||||
case rfc2866.AcctStatusType_Value_AccountingOn, rfc2866.AcctStatusType_Value_AccountingOff:
|
||||
// By default, no Accounting-On or Accounting-Off messages are sent (no acct-on-off).
|
||||
default:
|
||||
err = fmt.Errorf("unsupport statusType = %v", statusType)
|
||||
}
|
||||
}
|
||||
|
@ -29,11 +29,7 @@ func TestAccessRequestRejected(t *testing.T) {
|
||||
packet := radius.New(radius.CodeAccessRequest, []byte(`secret`))
|
||||
rfc2865.UserName_SetString(packet, "admin")
|
||||
rfc2865.UserPassword_SetString(packet, "12345")
|
||||
vsa, err := radius.NewVendorSpecific(OrganizationVendorID, []byte("built-in"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
packet.Add(rfc2865.VendorSpecific_Type, vsa)
|
||||
rfc2865.Class_SetString(packet, "built-in")
|
||||
response, err := radius.Exchange(context.Background(), packet, "localhost:1812")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -47,11 +43,7 @@ func TestAccessRequestAccepted(t *testing.T) {
|
||||
packet := radius.New(radius.CodeAccessRequest, []byte(`secret`))
|
||||
rfc2865.UserName_SetString(packet, "admin")
|
||||
rfc2865.UserPassword_SetString(packet, "123")
|
||||
vsa, err := radius.NewVendorSpecific(OrganizationVendorID, []byte("built-in"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
packet.Add(rfc2865.VendorSpecific_Type, vsa)
|
||||
rfc2865.Class_SetString(packet, "built-in")
|
||||
response, err := radius.Exchange(context.Background(), packet, "localhost:1812")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -15,26 +15,53 @@
|
||||
package radius
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"layeh.com/radius"
|
||||
"layeh.com/radius/rfc2865"
|
||||
"layeh.com/radius/rfc2866"
|
||||
"layeh.com/radius/rfc2869"
|
||||
)
|
||||
|
||||
const (
|
||||
OrganizationVendorID = uint32(100)
|
||||
)
|
||||
|
||||
func parseOrganization(p *radius.Packet) string {
|
||||
for _, avp := range p.Attributes {
|
||||
if avp.Type == rfc2865.VendorSpecific_Type {
|
||||
attr := avp.Attribute
|
||||
vendorId, value, err := radius.VendorSpecific(attr)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
if vendorId == OrganizationVendorID {
|
||||
return string(value)
|
||||
}
|
||||
}
|
||||
func GetAccountingFromRequest(r *radius.Request) *object.RadiusAccounting {
|
||||
acctInputOctets := int(rfc2866.AcctInputOctets_Get(r.Packet))
|
||||
acctInputGigawords := int(rfc2869.AcctInputGigawords_Get(r.Packet))
|
||||
acctOutputOctets := int(rfc2866.AcctOutputOctets_Get(r.Packet))
|
||||
acctOutputGigawords := int(rfc2869.AcctOutputGigawords_Get(r.Packet))
|
||||
organization := rfc2865.Class_GetString(r.Packet)
|
||||
getAcctStartTime := func(sessionTime int) time.Time {
|
||||
m, _ := time.ParseDuration(fmt.Sprintf("-%ds", sessionTime))
|
||||
return time.Now().Add(m)
|
||||
}
|
||||
return ""
|
||||
ra := &object.RadiusAccounting{
|
||||
Owner: organization,
|
||||
Name: "ra_" + util.GenerateId()[:6],
|
||||
CreatedTime: time.Now(),
|
||||
|
||||
Username: rfc2865.UserName_GetString(r.Packet),
|
||||
ServiceType: int64(rfc2865.ServiceType_Get(r.Packet)),
|
||||
|
||||
NasId: rfc2865.NASIdentifier_GetString(r.Packet),
|
||||
NasIpAddr: rfc2865.NASIPAddress_Get(r.Packet).String(),
|
||||
NasPortId: rfc2869.NASPortID_GetString(r.Packet),
|
||||
NasPortType: int64(rfc2865.NASPortType_Get(r.Packet)),
|
||||
NasPort: int64(rfc2865.NASPort_Get(r.Packet)),
|
||||
|
||||
FramedIpAddr: rfc2865.FramedIPAddress_Get(r.Packet).String(),
|
||||
FramedIpNetmask: rfc2865.FramedIPNetmask_Get(r.Packet).String(),
|
||||
|
||||
AcctSessionId: rfc2866.AcctSessionID_GetString(r.Packet),
|
||||
AcctSessionTime: int64(rfc2866.AcctSessionTime_Get(r.Packet)),
|
||||
AcctInputTotal: int64(acctInputOctets) + int64(acctInputGigawords)*4*1024*1024*1024,
|
||||
AcctOutputTotal: int64(acctOutputOctets) + int64(acctOutputGigawords)*4*1024*1024*1024,
|
||||
AcctInputPackets: int64(rfc2866.AcctInputPackets_Get(r.Packet)),
|
||||
AcctOutputPackets: int64(rfc2866.AcctInputPackets_Get(r.Packet)),
|
||||
AcctStartTime: getAcctStartTime(int(rfc2866.AcctSessionTime_Get(r.Packet))),
|
||||
AcctTerminateCause: int64(rfc2866.AcctTerminateCause_Get(r.Packet)),
|
||||
LastUpdate: time.Now(),
|
||||
}
|
||||
return ra
|
||||
}
|
||||
|
Reference in New Issue
Block a user