feat: support Radius Challenge/Response for MFA (RFC2865) feature request (#3093)

* feat: support RFC2865 for radius server when user enable TOTP mfa

* fix: fix linter err
This commit is contained in:
DacongDA 2024-08-01 22:02:49 +08:00 committed by GitHub
parent feff47d2dc
commit ac102480c7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -18,6 +18,7 @@ import (
"fmt"
"log"
"strings"
"time"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/object"
@ -27,6 +28,14 @@ import (
"layeh.com/radius/rfc2866"
)
var StateMap map[string]AccessStateContent
const StateExpiredTime = time.Second * 120
type AccessStateContent struct {
ExpiredAt time.Time
}
func StartRadiusServer() {
secret := conf.GetConfigString("radiusSecret")
server := radius.PacketServer{
@ -55,6 +64,7 @@ func handleAccessRequest(w radius.ResponseWriter, r *radius.Request) {
username := rfc2865.UserName_GetString(r.Packet)
password := rfc2865.UserPassword_GetString(r.Packet)
organization := rfc2865.Class_GetString(r.Packet)
state := rfc2865.State_GetString(r.Packet)
log.Printf("handleAccessRequest() username=%v, org=%v, password=%v", username, organization, password)
if organization == "" {
@ -62,12 +72,75 @@ func handleAccessRequest(w radius.ResponseWriter, r *radius.Request) {
return
}
_, err := object.CheckUserPassword(organization, username, password, "en")
var user *object.User
var err error
if state == "" {
user, err = object.CheckUserPassword(organization, username, password, "en")
} else {
user, err = object.GetUser(fmt.Sprintf("%s/%s", organization, username))
}
if err != nil {
w.Write(r.Response(radius.CodeAccessReject))
return
}
if user.IsMfaEnabled() {
mfaProp := user.GetMfaProps(object.TotpType, false)
if mfaProp == nil {
w.Write(r.Response(radius.CodeAccessReject))
return
}
if StateMap == nil {
StateMap = map[string]AccessStateContent{}
}
if state != "" {
stateContent, ok := StateMap[state]
if !ok {
w.Write(r.Response(radius.CodeAccessReject))
return
}
delete(StateMap, state)
if stateContent.ExpiredAt.Before(time.Now()) {
w.Write(r.Response(radius.CodeAccessReject))
return
}
mfaUtil := object.GetMfaUtil(mfaProp.MfaType, mfaProp)
if mfaUtil.Verify(password) != nil {
w.Write(r.Response(radius.CodeAccessReject))
return
}
w.Write(r.Response(radius.CodeAccessAccept))
return
}
responseState := util.GenerateId()
StateMap[responseState] = AccessStateContent{
time.Now().Add(StateExpiredTime),
}
err = rfc2865.State_Set(r.Packet, []byte(responseState))
if err != nil {
w.Write(r.Response(radius.CodeAccessReject))
return
}
err = rfc2865.ReplyMessage_Set(r.Packet, []byte("please enter OTP"))
if err != nil {
w.Write(r.Response(radius.CodeAccessReject))
return
}
r.Packet.Code = radius.CodeAccessChallenge
w.Write(r.Packet)
}
w.Write(r.Response(radius.CodeAccessAccept))
}