diff --git a/.gitignore b/.gitignore index e26bd7b..b52a489 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,6 @@ # Dependency directories (remove the comment below to include it) vendor/ - +ui/ .idea/ anylink \ No newline at end of file diff --git a/README.md b/README.md index 4f02bfa..62d5f7d 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,46 @@ # AnyLink -AnyLink 是一个企业级远程办公vpn软件,可以支持多人同时在线使用。 +[](https://pkg.go.dev/github.com/bjdgyc/anylink) + +AnyLink 是一个企业级远程办公ssl vpn软件,可以支持多人同时在线使用。 + +## Repo + +> github: https://github.com/bjdgyc/anylink + +> gitee: https://gitee.com/bjdgyc/anylink ## Introduction -AnyLink 基于 [ietf-openconnect](https://tools.ietf.org/html/draft-mavrogiannopoulos-openconnect-02) 协议开发,并且借鉴了 [ocserv](http://ocserv.gitlab.io/www/index.html) 的开发思路,使其可以同时兼容 AnyConnect 客户端。 +AnyLink 基于 [ietf-openconnect](https://tools.ietf.org/html/draft-mavrogiannopoulos-openconnect-02) +协议开发,并且借鉴了 [ocserv](http://ocserv.gitlab.io/www/index.html) 的开发思路,使其可以同时兼容 AnyConnect 客户端。 AnyLink 使用TLS/DTLS进行数据加密,因此需要RSA或ECC证书,可以通过 Let's Encrypt 和 TrustAsia 申请免费的SSL证书。 AnyLink 服务端仅在CentOS7测试通过,如需要安装在其他系统,需要服务端支持tun/tap功能、ip设置命令。 - ## Installation ``` +rootPath=`pwd` + git clone https://github.com/bjdgyc/anylink.git -cd anylink +git clone https://github.com/bjdgyc/anylink-web.git + +cd $rootPath/anylink-web +npm install +npm run build + +cd $rootPath/anylink go build -o anylink -ldflags "-X main.COMMIT_ID=`git rev-parse HEAD`" + +mkdir $linkPath/anylink-deploy +$linkPath/anylink-deploy +cp -r $rootPath/anylink-web/ui . +cp -r $rootPath/anylink/anylink . +cp -r $rootPath/anylink/conf . +cp -r $rootPath/anylink/downfiles . + #注意使用root权限运行 sudo ./anylink -conf="conf/server.toml" ``` @@ -28,17 +52,16 @@ sudo ./anylink -conf="conf/server.toml" - [x] 兼容AnyConnect - [x] 基于tun设备的nat访问模式 - [x] 基于tap设备的桥接访问模式 -- [x] 多用户支持 - [x] 支持 [proxy protocol v1](http://www.haproxy.org/download/2.2/doc/proxy-protocol.txt) 协议 +- [x] 用户组支持 +- [x] 多用户支持 +- [x] TOTP令牌支持 +- [x] 流量控制 +- [x] 后台管理界面 -- [ ] 用户组支持 -- [ ] TOTP令牌支持 -- [ ] 流量控制 - [ ] 访问权限管理 -- [ ] 后台管理界面 - [ ] DTLS-UDP通道 - ## Config 默认配置文件内有详细的注释,根据注释填写配置即可。 @@ -47,11 +70,9 @@ sudo ./anylink -conf="conf/server.toml" ## Setting -网络模式选择,需要配置 `link_mode` 参数,如 `link_mode="tun"`,`link_mode="tap"` 两种参数。 -不同的参数需要对服务器做相应的设置。 +网络模式选择,需要配置 `link_mode` 参数,如 `link_mode="tun"`,`link_mode="tap"` 两种参数。 不同的参数需要对服务器做相应的设置。 -建议优先选择tun模式,因客户端传输的是IP层数据,无须进行数据转换。 -tap模式是在用户态做的链路层到IP层的数据互相转换,性能会有所下降。 +建议优先选择tun模式,因客户端传输的是IP层数据,无须进行数据转换。 tap模式是在用户态做的链路层到IP层的数据互相转换,性能会有所下降。 如果需要在虚拟机内开启tap模式,请确认虚拟机的网卡开启混杂模式。 ### tun设置 @@ -69,18 +90,17 @@ tap模式是在用户态做的链路层到IP层的数据互相转换,性能会 # eth0为服务器内网网卡 iptables -t nat -A POSTROUTING -s 192.168.10.0/255.255.255.0 -o eth0 -j MASQUERADE ``` - + 3. 使用AnyConnect客户端连接即可 - ### tap设置 - + 1. 创建桥接网卡 ``` 注意 server.toml 的ip参数,需要与 bridge.sh 的配置参数一致 ``` - -2. 修改 bridge-init.sh 内的参数 + +2. 修改 bridge.sh 内的参数 ``` # file: ./bridge.sh eth="eth0" @@ -89,13 +109,11 @@ tap模式是在用户态做的链路层到IP层的数据互相转换,性能会 eth_broadcast="192.168.1.255" eth_gateway="192.168.1.1" ``` - + 3. 执行 bridge.sh 文件 ``` sh bridge.sh ``` - - ## License diff --git a/admin/api_base.go b/admin/api_base.go new file mode 100644 index 0000000..805b9d4 --- /dev/null +++ b/admin/api_base.go @@ -0,0 +1,80 @@ +package admin + +import ( + "fmt" + "net/http" + "time" + + "github.com/bjdgyc/anylink/pkg/utils" + + "github.com/bjdgyc/anylink/base" + "github.com/gorilla/mux" +) + +// 登陆接口 +func Login(w http.ResponseWriter, r *http.Request) { + // TODO 调试信息输出 + // hd, _ := httputil.DumpRequest(r, true) + // fmt.Println("DumpRequest: ", string(hd)) + + r.ParseForm() + admin_user := r.PostFormValue("admin_user") + admin_pass := r.PostFormValue("admin_pass") + + // 认证错误 + if !(admin_user == base.Cfg.AdminUser && + utils.PasswordVerify(admin_pass, base.Cfg.AdminPass)) { + RespError(w, RespUserOrPassErr) + return + } + + // token有效期 + expiresAt := time.Now().Unix() + 3600*3 + jwtData := map[string]interface{}{"admin_user": admin_user} + tokenString, err := SetJwtData(jwtData, expiresAt) + if err != nil { + RespError(w, 1, err) + return + } + + data := make(map[string]interface{}) + data["token"] = tokenString + data["admin_user"] = admin_user + data["expires_at"] = expiresAt + + RespSucess(w, data) +} + +func authMiddleware(next http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Access-Control-Allow-Methods", "GET,POST,OPTIONS") + w.Header().Set("Access-Control-Allow-Headers", "*") + if r.Method == http.MethodOptions { + return + } + + route := mux.CurrentRoute(r) + name := route.GetName() + // fmt.Println("bb", r.URL.Path, name) + if name == "login" || name == "static" { + // 不进行鉴权 + next.ServeHTTP(w, r) + return + } + + // 进行登陆鉴权 + jwtToken := r.Header.Get("Jwt") + if jwtToken == "" { + jwtToken = r.FormValue("jwt") + } + data, err := GetJwtData(jwtToken) + if err != nil || base.Cfg.AdminUser != fmt.Sprint(data["admin_user"]) { + w.WriteHeader(http.StatusUnauthorized) + return + } + next.ServeHTTP(w, r) + } + + return http.HandlerFunc(fn) +} diff --git a/admin/api_group.go b/admin/api_group.go new file mode 100644 index 0000000..ba9302a --- /dev/null +++ b/admin/api_group.go @@ -0,0 +1,108 @@ +package admin + +import ( + "encoding/json" + "io/ioutil" + "net/http" + "strconv" + + "github.com/bjdgyc/anylink/dbdata" +) + +func GroupList(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + pageS := r.FormValue("page") + page, _ := strconv.Atoi(pageS) + if page < 1 { + page = 1 + } + + var pageSize = dbdata.PageSize + + count := dbdata.CountAll(&dbdata.Group{}) + + var datas []dbdata.Group + err := dbdata.All(&datas, pageSize, page) + if err != nil { + RespError(w, RespInternalErr, err) + return + } + + data := map[string]interface{}{ + "count": count, + "page_size": pageSize, + "datas": datas, + } + + RespSucess(w, data) +} + +func GroupNames(w http.ResponseWriter, r *http.Request) { + var names = dbdata.GetGroupNames() + data := map[string]interface{}{ + "count": len(names), + "page_size": 0, + "datas": names, + } + RespSucess(w, data) +} + +func GroupDetail(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + idS := r.FormValue("id") + id, _ := strconv.Atoi(idS) + if id < 1 { + RespError(w, RespParamErr, "Id错误") + return + } + + var data dbdata.Group + err := dbdata.One("Id", id, &data) + if err != nil { + RespError(w, RespInternalErr, err) + return + } + + RespSucess(w, data) +} + +func GroupSet(w http.ResponseWriter, r *http.Request) { + body, err := ioutil.ReadAll(r.Body) + if err != nil { + RespError(w, RespInternalErr, err) + return + } + defer r.Body.Close() + v := &dbdata.Group{} + err = json.Unmarshal(body, v) + if err != nil { + RespError(w, RespInternalErr, err) + return + } + + err = dbdata.SetGroup(v) + if err != nil { + RespError(w, RespInternalErr, err) + return + } + + RespSucess(w, nil) +} + +func GroupDel(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + idS := r.FormValue("id") + id, _ := strconv.Atoi(idS) + if id < 1 { + RespError(w, RespParamErr, "Id错误") + return + } + + data := dbdata.Group{Id: id} + err := dbdata.Del(&data) + if err != nil { + RespError(w, RespInternalErr, err) + return + } + RespSucess(w, nil) +} diff --git a/admin/api_ip_map.go b/admin/api_ip_map.go new file mode 100644 index 0000000..ca4a024 --- /dev/null +++ b/admin/api_ip_map.go @@ -0,0 +1,111 @@ +package admin + +import ( + "encoding/json" + "io/ioutil" + "net/http" + "strconv" + "time" + + "github.com/bjdgyc/anylink/dbdata" +) + +func UserIpMapList(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + pageS := r.FormValue("page") + page, _ := strconv.Atoi(pageS) + if page < 1 { + page = 1 + } + + var pageSize = dbdata.PageSize + + count := dbdata.CountAll(&dbdata.IpMap{}) + + var datas []dbdata.IpMap + err := dbdata.All(&datas, pageSize, page) + if err != nil { + RespError(w, RespInternalErr, err) + return + } + + data := map[string]interface{}{ + "count": count, + "page_size": pageSize, + "datas": datas, + } + + RespSucess(w, data) +} + +func UserIpMapDetail(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + idS := r.FormValue("id") + id, _ := strconv.Atoi(idS) + if id < 1 { + RespError(w, RespParamErr, "用户名错误") + return + } + + var data dbdata.IpMap + err := dbdata.One("Id", id, &data) + if err != nil { + RespError(w, RespInternalErr, err) + return + } + + RespSucess(w, data) +} + +func UserIpMapSet(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + + body, err := ioutil.ReadAll(r.Body) + if err != nil { + RespError(w, RespInternalErr, err) + return + } + defer r.Body.Close() + v := &dbdata.IpMap{} + err = json.Unmarshal(body, v) + if err != nil { + RespError(w, RespInternalErr, err) + return + } + + // fmt.Println(v, len(v.Ip), len(v.MacAddr)) + + if len(v.IpAddr) < 4 || len(v.MacAddr) < 6 { + RespError(w, RespParamErr, "IP或MAC错误") + return + } + + v.UpdatedAt = time.Now() + err = dbdata.Save(v) + + if err != nil { + RespError(w, RespInternalErr, err) + return + } + + RespSucess(w, nil) +} + +func UserIpMapDel(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + idS := r.FormValue("id") + id, _ := strconv.Atoi(idS) + + if id < 1 { + RespError(w, RespParamErr, "IP映射id错误") + return + } + + data := dbdata.IpMap{Id: id} + err := dbdata.Del(&data) + if err != nil { + RespError(w, RespInternalErr, err) + return + } + RespSucess(w, nil) +} diff --git a/admin/api_other.go b/admin/api_other.go new file mode 100644 index 0000000..00f926a --- /dev/null +++ b/admin/api_other.go @@ -0,0 +1,62 @@ +package admin + +import ( + "encoding/json" + "io/ioutil" + "net/http" + + "github.com/bjdgyc/anylink/dbdata" +) + +func setOtherGet(data interface{}, w http.ResponseWriter) { + err := dbdata.SettingGet(data) + if err != nil && !dbdata.CheckErrNotFound(err) { + RespError(w, RespInternalErr, err) + return + } + RespSucess(w, data) +} + +func setOtherEdit(data interface{}, w http.ResponseWriter, r *http.Request) { + body, err := ioutil.ReadAll(r.Body) + if err != nil { + RespError(w, RespInternalErr, err) + return + } + defer r.Body.Close() + + err = json.Unmarshal(body, data) + if err != nil { + RespError(w, RespInternalErr, err) + return + } + + // fmt.Println(data) + + err = dbdata.SettingSet(data) + if err != nil { + RespError(w, RespInternalErr, err) + return + } + RespSucess(w, data) +} + +func SetOtherSmtp(w http.ResponseWriter, r *http.Request) { + data := &dbdata.SettingSmtp{} + setOtherGet(data, w) +} + +func SetOtherSmtpEdit(w http.ResponseWriter, r *http.Request) { + data := &dbdata.SettingSmtp{} + setOtherEdit(data, w, r) +} + +func SetOther(w http.ResponseWriter, r *http.Request) { + data := &dbdata.SettingOther{} + setOtherGet(data, w) +} + +func SetOtherEdit(w http.ResponseWriter, r *http.Request) { + data := &dbdata.SettingOther{} + setOtherEdit(data, w, r) +} diff --git a/admin/api_set.go b/admin/api_set.go new file mode 100644 index 0000000..6be5737 --- /dev/null +++ b/admin/api_set.go @@ -0,0 +1,95 @@ +package admin + +import ( + "encoding/json" + "fmt" + "net/http" + "runtime" + + "github.com/bjdgyc/anylink/dbdata" + "github.com/bjdgyc/anylink/sessdata" + + "github.com/bjdgyc/anylink/base" + "github.com/bjdgyc/anylink/pkg/utils" + "github.com/shirou/gopsutil/cpu" + "github.com/shirou/gopsutil/disk" + "github.com/shirou/gopsutil/host" + "github.com/shirou/gopsutil/load" + "github.com/shirou/gopsutil/mem" +) + +func SetHome(w http.ResponseWriter, r *http.Request) { + data := make(map[string]interface{}) + + sess := sessdata.OnlineSess() + + data["counts"] = map[string]int{ + "online": len(sess), + "user": dbdata.CountAll(&dbdata.User{}), + "group": dbdata.CountAll(&dbdata.Group{}), + "ip_map": dbdata.CountAll(&dbdata.IpMap{}), + } + + RespSucess(w, data) +} + +func SetSystem(w http.ResponseWriter, r *http.Request) { + data := make(map[string]interface{}) + + m, _ := mem.VirtualMemory() + data["mem"] = map[string]interface{}{ + "total": utils.HumanByte(m.Total), + "free": utils.HumanByte(m.Free), + "percent": decimal(m.UsedPercent), + } + + d, _ := disk.Usage("/") + data["disk"] = map[string]interface{}{ + "total": utils.HumanByte(d.Total), + "free": utils.HumanByte(d.Free), + "percent": decimal(d.UsedPercent), + } + + cc, _ := cpu.Counts(true) + c, _ := cpu.Info() + ci := c[0] + cpuUsedPercent, _ := cpu.Percent(0, false) + cup := cpuUsedPercent[0] + if cup == 0 { + cup = 1 + } + data["cpu"] = map[string]interface{}{ + "core": cc, + "modelName": ci.ModelName, + "ghz": fmt.Sprintf("%.2f GHz", ci.Mhz/1000), + "percent": decimal(cup), + } + + hi, _ := host.Info() + l, _ := load.Avg() + data["sys"] = map[string]interface{}{ + "goOs": runtime.GOOS, + "goArch": runtime.GOARCH, + "goVersion": runtime.Version(), + "goroutine": runtime.NumGoroutine(), + + "hostname": hi.Hostname, + "platform": fmt.Sprintf("%v %v %v", hi.Platform, hi.PlatformFamily, hi.PlatformVersion), + "kernel": hi.KernelVersion, + + "load": fmt.Sprint(l.Load1, l.Load5, l.Load15), + } + + RespSucess(w, data) +} + +func SetSoft(w http.ResponseWriter, r *http.Request) { + datas := base.ServerCfg2Slice() + b, _ := json.Marshal(datas) + w.Write(b) +} + +func decimal(f float64) float64 { + i := int(f * 100) + return float64(i) / 100 +} diff --git a/admin/api_user.go b/admin/api_user.go new file mode 100644 index 0000000..6e3db9e --- /dev/null +++ b/admin/api_user.go @@ -0,0 +1,226 @@ +package admin + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "strconv" + "strings" + "text/template" + "time" + + "github.com/bjdgyc/anylink/base" + "github.com/bjdgyc/anylink/dbdata" + "github.com/bjdgyc/anylink/sessdata" + "github.com/skip2/go-qrcode" +) + +func UserList(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + prefix := r.FormValue("prefix") + pageS := r.FormValue("page") + page, _ := strconv.Atoi(pageS) + if page < 1 { + page = 1 + } + + var ( + pageSize = dbdata.PageSize + count int + datas []dbdata.User + err error + ) + + // 查询前缀匹配 + if len(prefix) > 0 { + count = pageSize + err = dbdata.Prefix("Username", prefix, &datas, pageSize, 1) + } else { + count = dbdata.CountAll(&dbdata.User{}) + err = dbdata.All(&datas, pageSize, page) + } + + if err != nil && !dbdata.CheckErrNotFound(err) { + RespError(w, RespInternalErr, err) + return + } + + data := map[string]interface{}{ + "count": count, + "page_size": pageSize, + "datas": datas, + } + + RespSucess(w, data) +} + +func UserDetail(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + idS := r.FormValue("id") + id, _ := strconv.Atoi(idS) + if id < 1 { + RespError(w, RespParamErr, "用户名错误") + return + } + + var user dbdata.User + err := dbdata.One("Id", id, &user) + if err != nil { + RespError(w, RespInternalErr, err) + return + } + + RespSucess(w, user) +} + +func UserSet(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + + body, err := ioutil.ReadAll(r.Body) + if err != nil { + RespError(w, RespInternalErr, err) + return + } + defer r.Body.Close() + data := &dbdata.User{} + err = json.Unmarshal(body, data) + if err != nil { + RespError(w, RespInternalErr, err) + return + } + + err = dbdata.SetUser(data) + if err != nil { + RespError(w, RespInternalErr, err) + return + } + + // 发送邮件 + if data.SendEmail { + userAccountMail(data) + } + + RespSucess(w, nil) +} + +func UserDel(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + idS := r.FormValue("id") + id, _ := strconv.Atoi(idS) + + if id < 1 { + RespError(w, RespParamErr, "用户id错误") + return + } + + user := dbdata.User{Id: id} + err := dbdata.Del(&user) + if err != nil { + RespError(w, RespInternalErr, err) + return + } + RespSucess(w, nil) +} + +func UserOtpQr(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + b64 := r.FormValue("b64") + idS := r.FormValue("id") + id, _ := strconv.Atoi(idS) + var user dbdata.User + err := dbdata.One("Id", id, &user) + if err != nil { + RespError(w, RespInternalErr, err) + return + } + + issuer := base.Cfg.Issuer + qrstr := fmt.Sprintf("otpauth://totp/%s:%s?issuer=%s&secret=%s", issuer, user.Email, issuer, user.OtpSecret) + qr, _ := qrcode.New(qrstr, qrcode.High) + + if b64 == "1" { + data, _ := qr.PNG(300) + s := base64.StdEncoding.EncodeToString(data) + fmt.Fprint(w, s) + } else { + qr.Write(300, w) + } + +} + +// 在线用户 +func UserOnline(w http.ResponseWriter, r *http.Request) { + datas := sessdata.OnlineSess() + + data := map[string]interface{}{ + "count": len(datas), + "page_size": dbdata.PageSize, + "datas": datas, + } + + RespSucess(w, data) +} + +func UserOffline(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + token := r.FormValue("token") + sessdata.CloseSess(token) + RespSucess(w, nil) +} + +type userAccountMailData struct { + Issuer string + LinkAddr string + Group string + Username string + PinCode string + OtpImg string +} + +func userAccountMail(user *dbdata.User) error { + // 平台通知 + htmlBody := ` + + +
+ +您好:
+您的{{.Issuer}}账号已经审核开通。
+
+ 登陆地址: {{.LinkAddr}}
+ 用户组: {{.Group}}
+ 用户名: {{.Username}}
+ 用户PIN码: {{.PinCode}}
+ 用户动态码(3天后失效):
+
+
+ 软件下载地址: https://gitee.com/bjdgyc/anylink-soft/blob/master/README.md +
` diff --git a/dbdata/db_orm.go b/dbdata/db_orm.go new file mode 100644 index 0000000..e7d9a1e --- /dev/null +++ b/dbdata/db_orm.go @@ -0,0 +1,66 @@ +package dbdata + +import "github.com/asdine/storm/v3/index" + +const PageSize = 10 + +func Save(data interface{}) error { + return sdb.Save(data) +} + +func Update(data interface{}) error { + return sdb.Update(data) +} + +func UpdateField(data interface{}, fieldName string, value interface{}) error { + return sdb.UpdateField(data, fieldName, value) +} + +func Del(data interface{}) error { + return sdb.DeleteStruct(data) +} + +func Set(bucket, key string, data interface{}) error { + return sdb.Set(bucket, key, data) +} + +func Get(bucket, key string, data interface{}) error { + return sdb.Get(bucket, key, data) +} + +func CountAll(data interface{}) int { + n, _ := sdb.Count(data) + return n +} + +func One(fieldName string, value interface{}, to interface{}) error { + return sdb.One(fieldName, value, to) +} + +func Find(fieldName string, value interface{}, to interface{}, options ...func(q *index.Options)) error { + return sdb.Find(fieldName, value, to, options...) +} + +func All(to interface{}, limit, page int) error { + opt := getOpt(limit, page) + return sdb.All(to, opt) +} + +func Prefix(fieldName string, prefix string, to interface{}, limit, page int) error { + opt := getOpt(limit, page) + return sdb.Prefix(fieldName, prefix, to, opt) +} + +func getOpt(limit, page int) func(*index.Options) { + skip := (page - 1) * limit + opt := func(opt *index.Options) { + opt.Reverse = true + if limit > 0 { + opt.Limit = limit + } + if skip > 0 { + opt.Skip = skip + } + } + return opt +} diff --git a/dbdata/db_test.go b/dbdata/db_test.go index 1423d1e..60c5c72 100644 --- a/dbdata/db_test.go +++ b/dbdata/db_test.go @@ -1,23 +1,22 @@ package dbdata import ( - "net" "os" "path" "testing" - "github.com/bjdgyc/anylink/common" + "github.com/bjdgyc/anylink/base" "github.com/stretchr/testify/assert" ) func preIpData() { tmpDb := path.Join(os.TempDir(), "anylink_test.db") - common.ServerCfg.DbFile = tmpDb + base.Cfg.DbFile = tmpDb initDb() } func closeIpdata() { - db.Close() + sdb.Close() tmpDb := path.Join(os.TempDir(), "anylink_test.db") os.Remove(tmpDb) } @@ -27,37 +26,8 @@ func TestDb(t *testing.T) { preIpData() defer closeIpdata() - Set(BucketUser, "a", User{Username: "a"}) - Set(BucketUser, "b", User{Username: "b"}) - Set(BucketUser, "c", User{Username: "c"}) - Set(BucketUser, "d", User{Username: "d"}) - Set(BucketUser, "e", User{Username: "e"}) - Set(BucketUser, "f", User{Username: "f"}) - Set(BucketUser, "g", User{Username: "g"}) + u := User{Username: "a"} + Save(&u) - c := GetCount(BucketUser) - assert.Equal(c, 7) - Del(BucketUser, "g") - c = GetCount(BucketUser) - assert.Equal(c, 6) - - // 分页查询 - us := GetUsers("d", false) - assert.Equal(us[0].Username, "e") - assert.Equal(us[1].Username, "f") - us = GetUsers("d", true) - assert.Equal(us[0].Username, "c") - assert.Equal(us[1].Username, "b") - assert.Equal(us[2].Username, "a") - - mac1 := MacIp{Ip: net.ParseIP("192.168.3.11"), MacAddr: "mac1"} - mac2 := MacIp{Ip: net.ParseIP("192.168.3.12"), MacAddr: "mac2"} - Set(BucketMacIp, "mac1", mac1) - Set(BucketMacIp, "mac2", mac2) - - mp := GetAllMacIp() - assert.Equal(mp[0].MacAddr, "mac1") - assert.Equal(mp[1].MacAddr, "mac2") - - os.Exit(0) + assert.Equal(u.Id, 1) } diff --git a/dbdata/group.go b/dbdata/group.go index 9a8ebe4..e21b07e 100644 --- a/dbdata/group.go +++ b/dbdata/group.go @@ -1,36 +1,133 @@ package dbdata import ( - "encoding/json" + "errors" + "fmt" "net" + "strings" "time" + + "github.com/bjdgyc/anylink/base" ) -const BucketGroup = "group" +const ( + Allow = "allow" + Deny = "deny" +) + +type GroupLinkAcl struct { + // 自上而下匹配 默认 allow * * + Action string `json:"action"` // allow、deny + Val string `json:"val"` + Port uint8 `json:"port"` + IpNet *net.IPNet `json:"-"` +} + +type ValData struct { + Val string `json:"val"` +} type Group struct { - Id int - Name string - RouteInclude []string - RouteExclude []string - AllowLan bool - LinkAcl []struct { - Action string // allow、deny - IpNet string - IPNet net.IPNet - } - Bandwidth int // 带宽限制 - CreatedAt time.Time - UpdatedAt time.Time + Id int `json:"id" storm:"id,increment"` + Name string `json:"name" storm:"unique"` + Note string `json:"note"` + AllowLan bool `json:"allow_lan"` + ClientDns []ValData `json:"client_dns"` + RouteInclude []ValData `json:"route_include"` + RouteExclude []ValData `json:"route_exclude"` + LinkAcl []GroupLinkAcl `json:"link_acl"` + Bandwidth int `json:"bandwidth"` // 带宽限制 + Status int8 `json:"status"` // 1正常 + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` } -func GetGroups(lastKey string, prev bool) []Group { - res := getList(BucketUser, lastKey, prev) - datas := make([]Group, 0) - for _, data := range res { - d := Group{} - json.Unmarshal(data, &d) - datas = append(datas, d) +func GetGroupNames() []string { + var datas []Group + err := All(&datas, 0, 0) + if err != nil { + base.Error(err) + return nil } - return datas + var names []string + for _, v := range datas { + names = append(names, v.Name) + } + return names +} + +func SetGroup(g *Group) error { + var err error + if g.Name == "" { + return errors.New("用户组名错误") + } + + // 判断数据 + clientDns := []ValData{} + for _, v := range g.ClientDns { + if v.Val != "" { + clientDns = append(clientDns, v) + } + } + g.ClientDns = clientDns + + routeInclude := []ValData{} + for _, v := range g.RouteInclude { + if v.Val != "" { + v1, _ := parseIpNet(v.Val) + vn := ValData{Val: v1} + routeInclude = append(routeInclude, vn) + } + } + g.RouteInclude = routeInclude + routeExclude := []ValData{} + for _, v := range g.RouteExclude { + if v.Val != "" { + v1, _ := parseIpNet(v.Val) + vn := ValData{Val: v1} + routeExclude = append(routeExclude, vn) + } + } + g.RouteExclude = routeExclude + // 转换数据 + linkAcl := []GroupLinkAcl{} + for _, v := range g.LinkAcl { + if v.Val != "" { + v1, v2 := parseIpNet(v.Val) + if v2 != nil { + vn := v + vn.Val = v1 + vn.IpNet = v2 + linkAcl = append(linkAcl, vn) + } + } + } + g.LinkAcl = linkAcl + + g.UpdatedAt = time.Now() + err = Save(g) + + return err +} + +func parseIpNet(s string) (string, *net.IPNet) { + ips := strings.Split(s, "/") + if len(ips) != 2 { + return "", nil + } + ip := net.ParseIP(ips[0]) + mask := net.ParseIP(ips[1]) + + if strings.Contains(ips[0], ".") { + ip = ip.To4() + mask = mask.To4() + } + + ipmask := net.IPMask(mask) + ip0 := ip.Mask(ipmask) + + ipNetS := fmt.Sprintf("%s/%s", ip0, mask) + ipNet := &net.IPNet{IP: ip0, Mask: ipmask} + + return ipNetS, ipNet } diff --git a/dbdata/ip_map.go b/dbdata/ip_map.go new file mode 100644 index 0000000..2a81305 --- /dev/null +++ b/dbdata/ip_map.go @@ -0,0 +1,18 @@ +package dbdata + +import ( + "net" + "time" +) + +type IpMap struct { + Id int `json:"id" storm:"id,increment"` + IpAddr net.IP `json:"ip_addr" storm:"unique"` + MacAddr string `json:"mac_addr" storm:"unique"` + Username string `json:"username"` + Keep bool `json:"keep"` // 保留 ip-mac 绑定 + KeepTime time.Time `json:"keep_time"` + Note string `json:"note"` // 备注 + LastLogin time.Time `json:"last_login"` + UpdatedAt time.Time `json:"updated_at"` +} diff --git a/dbdata/mac_ip.go b/dbdata/mac_ip.go deleted file mode 100644 index ab220f9..0000000 --- a/dbdata/mac_ip.go +++ /dev/null @@ -1,34 +0,0 @@ -package dbdata - -import ( - "encoding/json" - "net" - "time" - - bolt "go.etcd.io/bbolt" -) - -const BucketMacIp = "macIp" - -type MacIp struct { - IsActive bool // db存储没有使用 - Ip net.IP - MacAddr string - LastLogin time.Time -} - -func GetAllMacIp() []MacIp { - datas := make([]MacIp, 0) - db.View(func(tx *bolt.Tx) error { - bkt := tx.Bucket([]byte(BucketMacIp)) - bkt.ForEach(func(k, v []byte) error { - d := MacIp{} - json.Unmarshal(v, &d) - datas = append(datas, d) - return nil - }) - return nil - }) - - return datas -} diff --git a/dbdata/setting.go b/dbdata/setting.go new file mode 100644 index 0000000..acc89f0 --- /dev/null +++ b/dbdata/setting.go @@ -0,0 +1,46 @@ +package dbdata + +import ( + "reflect" +) + +const ( + SettingBucket = "SettingBucket" + Installed = "Installed" +) + +func StructName(data interface{}) string { + ref := reflect.ValueOf(data) + s := &ref + if s.Kind() == reflect.Ptr { + e := s.Elem() + s = &e + } + name := s.Type().Name() + return name +} + +func SettingSet(data interface{}) error { + key := StructName(data) + err := Set(SettingBucket, key, data) + return err +} + +func SettingGet(data interface{}) error { + key := StructName(data) + err := Get(SettingBucket, key, data) + return err +} + +type SettingSmtp struct { + Host string `json:"host"` + Port int `json:"port"` + Username string `json:"username"` + Password string `json:"password"` + From string `json:"from"` +} + +type SettingOther struct { + Banner string `json:"banner"` + AccountMail string `json:"account_mail"` +} diff --git a/dbdata/start.go b/dbdata/start.go index a9e14c2..c8110be 100644 --- a/dbdata/start.go +++ b/dbdata/start.go @@ -2,8 +2,9 @@ package dbdata func Start() { initDb() + initData() } func Stop() error { - return db.Close() + return sdb.Close() } diff --git a/dbdata/user.go b/dbdata/user.go index df59a00..cd54868 100644 --- a/dbdata/user.go +++ b/dbdata/user.go @@ -1,29 +1,98 @@ package dbdata import ( - "encoding/json" + "errors" "time" + + "github.com/bjdgyc/anylink/pkg/utils" + "github.com/xlzd/gotp" ) -const BucketUser = "user" - type User struct { - Id int - Username string - Password string - OtpSecret string - Group []string - // CreatedAt time.Time - UpdatedAt time.Time + Id int `json:"id" storm:"id,increment"` + Username string `json:"username" storm:"unique"` + Nickname string `json:"nickname"` + Email string `json:"email"` + // Password string `json:"password"` + PinCode string `json:"pin_code"` + OtpSecret string `json:"otp_secret"` + Groups []string `json:"groups"` + Status int8 `json:"status"` // 1正常 + SendEmail bool `json:"send_email"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` } -func GetUsers(lastKey string, prev bool) []User { - res := getList(BucketUser, lastKey, prev) - datas := make([]User, 0) - for _, data := range res { - d := User{} - json.Unmarshal(data, &d) - datas = append(datas, d) +// 验证用户登陆信息 +func CheckUser(name, pwd, group string) error { + // return nil + + pl := len(pwd) + if name == "" || pl < 6 { + return errors.New("密码错误") } - return datas + v := &User{} + err := One("Username", name, v) + if err != nil || v.Status != 1 { + return errors.New("用户名错误") + } + pass := pwd[:pl-6] + // if !utils.PasswordVerify(pass, v.Password) { + if pass != v.PinCode { + return errors.New("密码错误") + } + otp := pwd[pl-6:] + totp := gotp.NewDefaultTOTP(v.OtpSecret) + unix := time.Now().Unix() + verify := totp.Verify(otp, int(unix)) + if !verify { + return errors.New("动态码错误") + } + + // 判断用户组信息 + if !utils.InArrStr(v.Groups, group) { + return errors.New("用户组错误") + } + groupData := &Group{} + err = One("Name", group, groupData) + if err != nil || groupData.Status != 1 { + return errors.New("用户组错误") + } + return nil +} + +func SetUser(v *User) error { + var err error + if v.Username == "" || len(v.Groups) == 0 { + return errors.New("用户名或组错误") + } + + planPass := v.PinCode + // 自动生成密码 + if len(planPass) < 6 { + planPass = utils.RandomNum(8) + } + v.PinCode = planPass + + if v.OtpSecret == "" { + v.OtpSecret = gotp.RandomSecret(24) + } + + // 判断组是否有效 + ng := []string{} + groups := GetGroupNames() + for _, g := range v.Groups { + if utils.InArrStr(groups, g) { + ng = append(ng, g) + } + } + if len(ng) == 0 { + return errors.New("用户名或组错误") + } + v.Groups = ng + + v.UpdatedAt = time.Now() + err = Save(v) + + return err } diff --git a/downfiles/.gitignore b/downfiles/.gitignore new file mode 100644 index 0000000..e2718ef --- /dev/null +++ b/downfiles/.gitignore @@ -0,0 +1,4 @@ +# Binaries for programs and plugins + +* +!.gitignore \ No newline at end of file diff --git a/go.mod b/go.mod index ef6046a..22af352 100644 --- a/go.mod +++ b/go.mod @@ -1,16 +1,25 @@ module github.com/bjdgyc/anylink -go 1.14 +go 1.15 require ( - github.com/google/gopacket v1.1.17 - github.com/pelletier/go-toml v1.8.0 + github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect + github.com/asdine/storm/v3 v3.2.1 + github.com/dgrijalva/jwt-go v3.2.0+incompatible + github.com/go-ole/go-ole v1.2.4 // indirect + github.com/google/gopacket v1.1.19 + github.com/gorilla/mux v1.8.0 + github.com/mojocn/base64Captcha v1.3.1 + github.com/pelletier/go-toml v1.8.1 + github.com/shirou/gopsutil v3.20.11+incompatible + github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091 github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 - github.com/stretchr/testify v1.5.1 + github.com/stretchr/testify v1.6.1 + github.com/xhit/go-simple-mail/v2 v2.6.0 github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119 go.etcd.io/bbolt v1.3.5 - golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 - golang.org/x/sys v0.0.0-20200817155316-9781c653f443 // indirect - golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e + golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9 + golang.org/x/net v0.0.0-20201209123823-ac852fbbde11 + golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 ) diff --git a/go.sum b/go.sum index 38a1be0..207da05 100644 --- a/go.sum +++ b/go.sum @@ -1,39 +1,96 @@ -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM= +github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/Sereal/Sereal v0.0.0-20190618215532-0b8ac451a863 h1:BRrxwOZBolJN4gIwvZMJY1tzqBvQgpaZiQRuIDD40jM= +github.com/Sereal/Sereal v0.0.0-20190618215532-0b8ac451a863/go.mod h1:D0JMgToj/WdxCgd30Kc1UcA9E+WdZoJqeVOuYW7iTBM= +github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= +github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/asdine/storm/v3 v3.2.1 h1:I5AqhkPK6nBZ/qJXySdI7ot5BlXSZ7qvDY1zAn5ZJac= +github.com/asdine/storm/v3 v3.2.1/go.mod h1:LEpXwGt4pIqrE/XcTvCnZHT5MgZCV6Ub9q7yQzOFWr0= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/google/gopacket v1.1.17 h1:rMrlX2ZY2UbvT+sdz3+6J+pp2z+msCq9MxTU6ymxbBY= -github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= -github.com/pelletier/go-toml v1.8.0 h1:Keo9qb7iRJs2voHvunFtuuYFsbWeOBh8/P9v/kVMFtw= -github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= +github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= +github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mojocn/base64Captcha v1.3.1 h1:2Wbkt8Oc8qjmNJ5GyOfSo4tgVQPsbKMftqASnq8GlT0= +github.com/mojocn/base64Captcha v1.3.1/go.mod h1:wAQCKEc5bDujxKRmbT6/vTnTt5CjStQ8bRfPWUuz/iY= +github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM= +github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/shirou/gopsutil v3.20.11+incompatible h1:LJr4ZQK4mPpIV5gOa4jCOKOGb4ty4DZO54I4FGqIpto= +github.com/shirou/gopsutil v3.20.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091 h1:1zN6ImoqhSJhN8hGXFaJlSC8msLmIbX8bFqOfWLKw0w= github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091/go.mod h1:N20Z5Y8oye9a7HmytmZ+tr8Q2vlP0tAHP13kTHzwvQY= github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D9tmUCz4VNwm9MfrtPr0SU2qSX8= github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= +github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= +github.com/xhit/go-simple-mail/v2 v2.6.0 h1:pvPmpDUUWy07cnTgwxwEe5fjdyYtETnxcvdGPQxtv/k= +github.com/xhit/go-simple-mail/v2 v2.6.0/go.mod h1:kA1XbQfCI4JxQ9ccSN6VFyIEkkugOm7YiPkA5hKiQn4= github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119 h1:YyPWX3jLOtYKulBR6AScGIs74lLrJcgeKRwcbAuQOG4= github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119/go.mod h1:/nuTSlK+okRfR/vnIPqR89fFKonnWPiZymN5ydRJkX8= +go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9 h1:sYNJzB4J8toYPQTM6pAkcmBRgw9SnQKP9oXCHfgy604= +golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/image v0.0.0-20190501045829-6d32002ffd75 h1:TbGuee8sSq15Iguxu4deQ7+Bqq/d2rsQejGcEtADAMQ= +golang.org/x/image v0.0.0-20190501045829-6d32002ffd75/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191105084925-a882066a44e0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11 h1:lwlPPsmjDKK0J6eG6xDWd5XPehI0R024zxjDnw3esPA= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200817155316-9781c653f443 h1:X18bCaipMcoJGm27Nv7zr4XYPKGUy92GtqboKC2Hxaw= -golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/handler/link_auth.go b/handler/link_auth.go index 0b369ac..9e82bdf 100644 --- a/handler/link_auth.go +++ b/handler/link_auth.go @@ -8,7 +8,8 @@ import ( "strings" "text/template" - "github.com/bjdgyc/anylink/common" + "github.com/bjdgyc/anylink/base" + "github.com/bjdgyc/anylink/dbdata" "github.com/bjdgyc/anylink/sessdata" ) @@ -40,7 +41,7 @@ func LinkAuth(w http.ResponseWriter, r *http.Request) { if cr.Type == "init" { w.WriteHeader(http.StatusOK) - data := RequestData{Group: cr.GroupSelect, Groups: common.ServerCfg.UserGroups} + data := RequestData{Group: cr.GroupSelect, Groups: dbdata.GetGroupNames()} tplRequest(tpl_request, w, data) return } @@ -52,22 +53,33 @@ func LinkAuth(w http.ResponseWriter, r *http.Request) { } // TODO 用户密码校验 - if !CheckUser(cr.Auth.Username, cr.Auth.Password, cr.GroupSelect) { + err = dbdata.CheckUser(cr.Auth.Username, cr.Auth.Password, cr.GroupSelect) + if err != nil { + base.Info(err) w.WriteHeader(http.StatusOK) - data := RequestData{Group: cr.GroupSelect, Groups: common.ServerCfg.UserGroups, Error: true} + data := RequestData{Group: cr.GroupSelect, Groups: dbdata.GetGroupNames(), Error: "用户名或密码错误"} tplRequest(tpl_request, w, data) return } + // if !ok { + // w.WriteHeader(http.StatusOK) + // data := RequestData{Group: cr.GroupSelect, Groups: base.Cfg.UserGroups, Error: "请先激活用户"} + // tplRequest(tpl_request, w, data) + // return + // } // 创建新的session信息 - sess := sessdata.NewSession() - sess.UserName = cr.Auth.Username + sess := sessdata.NewSession("") + sess.Username = cr.Auth.Username + sess.Group = cr.GroupSelect sess.MacAddr = strings.ToLower(cr.MacAddressList.MacAddress) sess.UniqueIdGlobal = cr.DeviceId.UniqueIdGlobal - cd := RequestData{SessionId: sess.Sid, SessionToken: sess.Sid + "@" + sess.Token, - Banner: common.ServerCfg.Banner} + other := &dbdata.SettingOther{} + dbdata.SettingGet(other) + rd := RequestData{SessionId: sess.Sid, SessionToken: sess.Sid + "@" + sess.Token, + Banner: other.Banner} w.WriteHeader(http.StatusOK) - tplRequest(tpl_complete, w, cd) + tplRequest(tpl_complete, w, rd) } const ( @@ -94,7 +106,8 @@ func tplRequest(typ int, w io.Writer, data RequestData) { type RequestData struct { Groups []string Group string - Error bool + Error string + // complete SessionId string SessionToken string @@ -116,7 +129,7 @@ var auth_request = `