From b06c035cce7fb3cc48e220c2c9f4d185de97f3aa Mon Sep 17 00:00:00 2001
From: lanrenwo <lanrenwo@users.noreply.github.com>
Date: Mon, 6 Jun 2022 21:25:19 +0800
Subject: [PATCH 1/3] =?UTF-8?q?1.=E9=87=8D=E6=9E=84=E8=AE=A4=E8=AF=81?=
 =?UTF-8?q?=E6=96=B9=E5=BC=8F=E7=9A=84=E4=BB=A3=E7=A0=81,=E6=96=B9?=
 =?UTF-8?q?=E4=BE=BF=E6=9C=AA=E6=9D=A5=E6=89=A9=E5=B1=95=202.=E8=A1=A5?=
 =?UTF-8?q?=E5=85=85=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 server/dbdata/group.go           | 45 ++++--------------
 server/dbdata/group_test.go      | 19 +++++++-
 server/dbdata/user.go            | 50 ++++----------------
 server/dbdata/user_test.go       | 18 ++++++++
 server/dbdata/userauth.go        | 15 ++++++
 server/dbdata/userauth_radius.go | 78 ++++++++++++++++++++++++++++++++
 6 files changed, 149 insertions(+), 76 deletions(-)
 create mode 100644 server/dbdata/userauth.go
 create mode 100644 server/dbdata/userauth_radius.go

diff --git a/server/dbdata/group.go b/server/dbdata/group.go
index 4effbd2..52524d1 100644
--- a/server/dbdata/group.go
+++ b/server/dbdata/group.go
@@ -1,7 +1,6 @@
 package dbdata
 
 import (
-	"encoding/json"
 	"errors"
 	"fmt"
 	"net"
@@ -33,11 +32,6 @@ type ValData struct {
 	Note   string `json:"note"`
 }
 
-type AuthRadius struct {
-	Addr   string `json:"addr"`
-	Secret string `json:"secret"`
-}
-
 // type Group struct {
 // 	Id               int                    `json:"id" xorm:"pk autoincr not null"`
 // 	Name             string                 `json:"name" xorm:"varchar(60) not null unique"`
@@ -154,23 +148,26 @@ func SetGroup(g *Group) error {
 	if err != nil {
 		return errors.New("排除域名有误:" + err.Error())
 	}
-	// 处理认证方式的逻辑
+	// 处理登入方式的逻辑
 	defAuth := map[string]interface{}{
 		"type": "local",
 	}
 	if len(g.Auth) == 0 {
 		g.Auth = defAuth
 	}
-	switch g.Auth["type"] {
-	case "local":
+	authType := g.Auth["type"].(string)
+	if authType == "local" {
 		g.Auth = defAuth
-	case "radius":
-		err = checkRadiusData(g.Auth)
+	} else {
+		_, ok := authRegistry[authType]
+		if !ok {
+			return errors.New("未知的认证方式: " + fmt.Sprintf("%s", g.Auth["type"]))
+		}
+		auth := makeInstance(authType).(IUserAuth)
+		err = auth.checkData(g.Auth)
 		if err != nil {
 			return err
 		}
-	default:
-		return errors.New("#" + fmt.Sprintf("%s", g.Auth["type"]) + "#未知的认证类型")
 	}
 
 	g.UpdatedAt = time.Now()
@@ -195,23 +192,6 @@ func parseIpNet(s string) (string, *net.IPNet, error) {
 	return ipMask, ipNet, nil
 }
 
-func checkRadiusData(auth map[string]interface{}) error {
-	radisConf := AuthRadius{}
-	bodyBytes, err := json.Marshal(auth["radius"])
-	if err != nil {
-		return errors.New("Radius的密钥/服务器地址填写有误")
-	}
-	json.Unmarshal(bodyBytes, &radisConf)
-	if !ValidateIpPort(radisConf.Addr) {
-		return errors.New("Radius的服务器地址填写有误")
-	}
-	// freeradius官网最大8000字符, 这里限制200
-	if len(radisConf.Secret) < 8 || len(radisConf.Secret) > 200 {
-		return errors.New("Radius的密钥长度需在8~200个字符之间")
-	}
-	return nil
-}
-
 func CheckDomainNames(domains string) error {
 	if domains == "" {
 		return nil
@@ -232,8 +212,3 @@ func ValidateDomainName(domain string) bool {
 	RegExp := regexp.MustCompile(`^([a-zA-Z0-9][-a-zA-Z0-9]{0,62}\.)+[A-Za-z]{2,18}$`)
 	return RegExp.MatchString(domain)
 }
-
-func ValidateIpPort(addr string) bool {
-	RegExp := regexp.MustCompile(`^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\:([0-9]|[1-9]\d{1,3}|[1-5]\d{4}|6[0-5]{2}[0-3][0-5])$$`)
-	return RegExp.MatchString(addr)
-}
diff --git a/server/dbdata/group_test.go b/server/dbdata/group_test.go
index ee1dee1..7d27c7f 100644
--- a/server/dbdata/group_test.go
+++ b/server/dbdata/group_test.go
@@ -24,8 +24,25 @@ func TestGetGroupNames(t *testing.T) {
 	err = SetGroup(&g3)
 	ast.Nil(err)
 
+	authData := map[string]interface{}{
+		"type": "radius",
+		"radius": map[string]string{
+			"addr":   "192.168.8.12:1044",
+			"secret": "43214132",
+		},
+	}
+	g4 := Group{Name: "g4", ClientDns: []ValData{{Val: "114.114.114.114"}}, Auth: authData}
+	err = SetGroup(&g4)
+	ast.Nil(err)
+	g5 := Group{Name: "g5", ClientDns: []ValData{{Val: "114.114.114.114"}}, DsIncludeDomains: "baidu.com,163.com"}
+	err = SetGroup(&g5)
+	ast.Nil(err)
+	g6 := Group{Name: "g6", ClientDns: []ValData{{Val: "114.114.114.114"}}, DsExcludeDomains: "com.cn,qq.com"}
+	err = SetGroup(&g6)
+	ast.Nil(err)
+
 	// 判断所有数据
-	gAll := []string{"g1", "g2", "g3"}
+	gAll := []string{"g1", "g2", "g3", "g4", "g5", "g6"}
 	gs := GetGroupNames()
 	for _, v := range gs {
 		ast.Equal(true, utils.InArrStr(gAll, v))
diff --git a/server/dbdata/user.go b/server/dbdata/user.go
index 3db7461..6add617 100644
--- a/server/dbdata/user.go
+++ b/server/dbdata/user.go
@@ -1,8 +1,6 @@
 package dbdata
 
 import (
-	"context"
-	"encoding/json"
 	"errors"
 	"fmt"
 	"sync"
@@ -10,8 +8,6 @@ import (
 
 	"github.com/bjdgyc/anylink/pkg/utils"
 	"github.com/xlzd/gotp"
-	"layeh.com/radius"
-	"layeh.com/radius/rfc2865"
 )
 
 // type User struct {
@@ -82,15 +78,18 @@ func CheckUser(name, pwd, group string) error {
 	if len(groupData.Auth) == 0 {
 		groupData.Auth["type"] = "local"
 	}
-	switch groupData.Auth["type"] {
-	case "", "local":
+	authType := groupData.Auth["type"].(string)
+	// 本地认证方式
+	if authType == "local" {
 		return checkLocalUser(name, pwd, group)
-	case "radius":
-		return checkRadiusUser(name, pwd, groupData.Auth)
-	default:
-		return fmt.Errorf("%s %s", name, "无效的认证类型")
 	}
-	return nil
+	// 其它认证方式, 支持自定义
+	_, ok := authRegistry[authType]
+	if !ok {
+		return fmt.Errorf("%s %s", "未知的认证方式: ", authType)
+	}
+	auth := makeInstance(authType).(IUserAuth)
+	return auth.checkUser(name, pwd, groupData.Auth)
 }
 
 // 验证本地用户登陆信息
@@ -135,35 +134,6 @@ func checkLocalUser(name, pwd, group string) error {
 	return nil
 }
 
-func checkRadiusUser(name string, pwd string, auth map[string]interface{}) error {
-	if _, ok := auth["radius"]; !ok {
-		fmt.Errorf("%s %s", name, "Radius的radius值不存在")
-	}
-	radiusConf := AuthRadius{}
-	bodyBytes, err := json.Marshal(auth["radius"])
-	if err != nil {
-		fmt.Errorf("%s %s", name, "Radius Marshal出现错误")
-	}
-	err = json.Unmarshal(bodyBytes, &radiusConf)
-	if err != nil {
-		fmt.Errorf("%s %s", name, "Radius Unmarshal出现错误")
-	}
-	// radius认证时,设置超时3秒
-	packet := radius.New(radius.CodeAccessRequest, []byte(radiusConf.Secret))
-	rfc2865.UserName_SetString(packet, name)
-	rfc2865.UserPassword_SetString(packet, pwd)
-	ctx, done := context.WithTimeout(context.Background(), 3*time.Second)
-	defer done()
-	response, err := radius.Exchange(ctx, packet, radiusConf.Addr)
-	if err != nil {
-		return fmt.Errorf("%s %s", name, "Radius服务器连接异常, 请检测服务器和端口")
-	}
-	if response.Code != radius.CodeAccessAccept {
-		return fmt.Errorf("%s %s", name, "Radius:用户名或密码错误")
-	}
-	return nil
-}
-
 var (
 	userOtpMux = sync.Mutex{}
 	userOtp    = map[string]time.Time{}
diff --git a/server/dbdata/user_test.go b/server/dbdata/user_test.go
index e076672..545ca45 100644
--- a/server/dbdata/user_test.go
+++ b/server/dbdata/user_test.go
@@ -40,4 +40,22 @@ func TestCheckUser(t *testing.T) {
 	_ = SetUser(&u)
 	err = CheckUser("aaa", u.PinCode, group)
 	ast.Nil(err)
+
+	// 添加一个radius组
+	group2 := "group2"
+	authData := map[string]interface{}{
+		"type": "radius",
+		"radius": map[string]string{
+			"addr":   "192.168.1.12:1044",
+			"secret": "43214132",
+		},
+	}
+	g2 := Group{Name: group2, Status: 1, ClientDns: dns, RouteInclude: route, Auth: authData}
+	err = SetGroup(&g2)
+	ast.Nil(err)
+	err = CheckUser("aaa", "bbbbbbb", group2)
+	if ast.NotNil(err) {
+		ast.Equal("aaa Radius服务器连接异常, 请检测服务器和端口", err.Error())
+
+	}
 }
diff --git a/server/dbdata/userauth.go b/server/dbdata/userauth.go
new file mode 100644
index 0000000..44ac09b
--- /dev/null
+++ b/server/dbdata/userauth.go
@@ -0,0 +1,15 @@
+package dbdata
+
+import "reflect"
+
+var authRegistry = make(map[string]reflect.Type)
+
+type IUserAuth interface {
+	checkData(authData map[string]interface{}) error
+	checkUser(name string, pwd string, authData map[string]interface{}) error
+}
+
+func makeInstance(name string) interface{} {
+	v := reflect.New(authRegistry[name]).Elem()
+	return v.Interface()
+}
diff --git a/server/dbdata/userauth_radius.go b/server/dbdata/userauth_radius.go
new file mode 100644
index 0000000..13b627d
--- /dev/null
+++ b/server/dbdata/userauth_radius.go
@@ -0,0 +1,78 @@
+package dbdata
+
+import (
+	"context"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"reflect"
+	"regexp"
+	"time"
+
+	"layeh.com/radius"
+	"layeh.com/radius/rfc2865"
+)
+
+type AuthRadius struct {
+	Addr   string `json:"addr"`
+	Secret string `json:"secret"`
+}
+
+func init() {
+	authRegistry["radius"] = reflect.TypeOf(AuthRadius{})
+}
+
+func (auth AuthRadius) checkData(authData map[string]interface{}) error {
+	authType := authData["type"].(string)
+	bodyBytes, err := json.Marshal(authData[authType])
+	if err != nil {
+		return errors.New("Radius的密钥/服务器地址填写有误")
+	}
+	json.Unmarshal(bodyBytes, &auth)
+	if !ValidateIpPort(auth.Addr) {
+		return errors.New("Radius的服务器地址填写有误")
+	}
+	// freeradius官网最大8000字符, 这里限制200
+	if len(auth.Secret) < 8 || len(auth.Secret) > 200 {
+		return errors.New("Radius的密钥长度需在8~200个字符之间")
+	}
+	return nil
+}
+
+func (auth AuthRadius) checkUser(name string, pwd string, authData map[string]interface{}) error {
+	pl := len(pwd)
+	if name == "" || pl < 1 {
+		return fmt.Errorf("%s %s", name, "密码错误")
+	}
+	authType := authData["type"].(string)
+	if _, ok := authData[authType]; !ok {
+		return fmt.Errorf("%s %s", name, "Radius的radius值不存在")
+	}
+	bodyBytes, err := json.Marshal(authData[authType])
+	if err != nil {
+		return fmt.Errorf("%s %s", name, "Radius Marshal出现错误")
+	}
+	err = json.Unmarshal(bodyBytes, &auth)
+	if err != nil {
+		return fmt.Errorf("%s %s", name, "Radius Unmarshal出现错误")
+	}
+	// radius认证时,设置超时3秒
+	packet := radius.New(radius.CodeAccessRequest, []byte(auth.Secret))
+	rfc2865.UserName_SetString(packet, name)
+	rfc2865.UserPassword_SetString(packet, pwd)
+	ctx, done := context.WithTimeout(context.Background(), 3*time.Second)
+	defer done()
+	response, err := radius.Exchange(ctx, packet, auth.Addr)
+	if err != nil {
+		return fmt.Errorf("%s %s", name, "Radius服务器连接异常, 请检测服务器和端口")
+	}
+	if response.Code != radius.CodeAccessAccept {
+		return fmt.Errorf("%s %s", name, "Radius:用户名或密码错误")
+	}
+	return nil
+}
+
+func ValidateIpPort(addr string) bool {
+	RegExp := regexp.MustCompile(`^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\:([0-9]|[1-9]\d{1,3}|[1-5]\d{4}|6[0-5]{2}[0-3][0-5])$$`)
+	return RegExp.MatchString(addr)
+}

From f46a30488ac0ac435c55658301828e70e86d2cde Mon Sep 17 00:00:00 2001
From: lanrenwo <lanrenwo@users.noreply.github.com>
Date: Mon, 6 Jun 2022 21:34:51 +0800
Subject: [PATCH 2/3] =?UTF-8?q?=E8=BF=81=E7=A7=BBValidateIpPort=E5=87=BD?=
 =?UTF-8?q?=E6=95=B0=E7=9A=84=E4=BD=8D=E7=BD=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 server/dbdata/userauth.go        | 10 +++++++++-
 server/dbdata/userauth_radius.go |  6 ------
 2 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/server/dbdata/userauth.go b/server/dbdata/userauth.go
index 44ac09b..190e863 100644
--- a/server/dbdata/userauth.go
+++ b/server/dbdata/userauth.go
@@ -1,6 +1,9 @@
 package dbdata
 
-import "reflect"
+import (
+	"reflect"
+	"regexp"
+)
 
 var authRegistry = make(map[string]reflect.Type)
 
@@ -13,3 +16,8 @@ func makeInstance(name string) interface{} {
 	v := reflect.New(authRegistry[name]).Elem()
 	return v.Interface()
 }
+
+func ValidateIpPort(addr string) bool {
+	RegExp := regexp.MustCompile(`^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\:([0-9]|[1-9]\d{1,3}|[1-5]\d{4}|6[0-5]{2}[0-3][0-5])$$`)
+	return RegExp.MatchString(addr)
+}
diff --git a/server/dbdata/userauth_radius.go b/server/dbdata/userauth_radius.go
index 13b627d..ef6691e 100644
--- a/server/dbdata/userauth_radius.go
+++ b/server/dbdata/userauth_radius.go
@@ -6,7 +6,6 @@ import (
 	"errors"
 	"fmt"
 	"reflect"
-	"regexp"
 	"time"
 
 	"layeh.com/radius"
@@ -71,8 +70,3 @@ func (auth AuthRadius) checkUser(name string, pwd string, authData map[string]in
 	}
 	return nil
 }
-
-func ValidateIpPort(addr string) bool {
-	RegExp := regexp.MustCompile(`^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\:([0-9]|[1-9]\d{1,3}|[1-5]\d{4}|6[0-5]{2}[0-3][0-5])$$`)
-	return RegExp.MatchString(addr)
-}

From 8ede613488a8fdb5cb90962e0b049d91ffecc583 Mon Sep 17 00:00:00 2001
From: lanrenwo <lanrenwo@users.noreply.github.com>
Date: Tue, 7 Jun 2022 09:46:45 +0800
Subject: [PATCH 3/3] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=94=A8=E6=88=B7?=
 =?UTF-8?q?=E7=BB=84=E5=81=9C=E7=94=A8=E6=97=B6=E7=9A=84=E7=99=BB=E5=BD=95?=
 =?UTF-8?q?=E9=97=AE=E9=A2=98=20+=20=E4=BC=98=E5=8C=96IUserAuth=E7=9A=84ch?=
 =?UTF-8?q?eckUser=E5=8F=82=E6=95=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 server/dbdata/user.go            | 16 +++++-----------
 server/dbdata/userauth.go        |  2 +-
 server/dbdata/userauth_radius.go |  8 ++++----
 3 files changed, 10 insertions(+), 16 deletions(-)

diff --git a/server/dbdata/user.go b/server/dbdata/user.go
index 6add617..7834013 100644
--- a/server/dbdata/user.go
+++ b/server/dbdata/user.go
@@ -66,13 +66,13 @@ func SetUser(v *User) error {
 	return err
 }
 
-// 验证用户登陆信息
+// 验证用户登录信息
 func CheckUser(name, pwd, group string) error {
 	// 获取登入的group数据
 	groupData := &Group{}
 	err := One("Name", group, groupData)
-	if err != nil {
-		return fmt.Errorf("%s %s", name, "No用户组")
+	if err != nil || groupData.Status != 1 {
+		return fmt.Errorf("%s - %s", name, "用户组错误")
 	}
 	// 初始化Auth
 	if len(groupData.Auth) == 0 {
@@ -89,10 +89,10 @@ func CheckUser(name, pwd, group string) error {
 		return fmt.Errorf("%s %s", "未知的认证方式: ", authType)
 	}
 	auth := makeInstance(authType).(IUserAuth)
-	return auth.checkUser(name, pwd, groupData.Auth)
+	return auth.checkUser(name, pwd, groupData)
 }
 
-// 验证本地用户登陆信息
+// 验证本地用户登录信息
 func checkLocalUser(name, pwd, group string) error {
 	// TODO 严重问题
 	// return nil
@@ -110,12 +110,6 @@ func checkLocalUser(name, pwd, group string) error {
 	if !utils.InArrStr(v.Groups, group) {
 		return fmt.Errorf("%s %s", name, "用户组错误")
 	}
-	groupData := &Group{}
-	err = One("Name", group, groupData)
-	if err != nil || groupData.Status != 1 {
-		return fmt.Errorf("%s - %s", name, "用户组错误")
-	}
-
 	// 判断otp信息
 	pinCode := pwd
 	if !v.DisableOtp {
diff --git a/server/dbdata/userauth.go b/server/dbdata/userauth.go
index 190e863..fbc3eb5 100644
--- a/server/dbdata/userauth.go
+++ b/server/dbdata/userauth.go
@@ -9,7 +9,7 @@ var authRegistry = make(map[string]reflect.Type)
 
 type IUserAuth interface {
 	checkData(authData map[string]interface{}) error
-	checkUser(name string, pwd string, authData map[string]interface{}) error
+	checkUser(name, pwd string, g *Group) error
 }
 
 func makeInstance(name string) interface{} {
diff --git a/server/dbdata/userauth_radius.go b/server/dbdata/userauth_radius.go
index ef6691e..4d15eb1 100644
--- a/server/dbdata/userauth_radius.go
+++ b/server/dbdata/userauth_radius.go
@@ -38,16 +38,16 @@ func (auth AuthRadius) checkData(authData map[string]interface{}) error {
 	return nil
 }
 
-func (auth AuthRadius) checkUser(name string, pwd string, authData map[string]interface{}) error {
+func (auth AuthRadius) checkUser(name, pwd string, g *Group) error {
 	pl := len(pwd)
 	if name == "" || pl < 1 {
 		return fmt.Errorf("%s %s", name, "密码错误")
 	}
-	authType := authData["type"].(string)
-	if _, ok := authData[authType]; !ok {
+	authType := g.Auth["type"].(string)
+	if _, ok := g.Auth[authType]; !ok {
 		return fmt.Errorf("%s %s", name, "Radius的radius值不存在")
 	}
-	bodyBytes, err := json.Marshal(authData[authType])
+	bodyBytes, err := json.Marshal(g.Auth[authType])
 	if err != nil {
 		return fmt.Errorf("%s %s", name, "Radius Marshal出现错误")
 	}