123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353 |
- package service
- import (
- "bytes"
- "errors"
- "fmt"
- "io/ioutil"
- "math/rand"
- "net/http"
- "strconv"
- "strings"
- "time"
- "unicode"
- "github.com/IceWhaleTech/CasaOS/pkg/config"
- command2 "github.com/IceWhaleTech/CasaOS/pkg/utils/command"
- httper2 "github.com/IceWhaleTech/CasaOS/pkg/utils/httper"
- "github.com/IceWhaleTech/CasaOS/pkg/zerotier"
- "github.com/PuerkitoBio/goquery"
- "github.com/tidwall/gjson"
- )
- type ZeroTierService interface {
- GetToken(username, pwd string) string
- ZeroTierRegister(email, lastName, firstName, password string) string
- ZeroTierNetworkList(token string) (interface{}, []string)
- ZeroTierJoinNetwork(networkId string)
- ZeroTierLeaveNetwork(networkId string)
- ZeroTierGetInfo(token, id string) (interface{}, []string)
- ZeroTierGetStatus(token string) interface{}
- EditNetwork(token string, data string, id string) interface{}
- CreateNetwork(token string) interface{}
- MemberList(token string, id string) interface{}
- EditNetworkMember(token string, data string, id, mId string) interface{}
- DeleteMember(token string, id, mId string) interface{}
- DeleteNetwork(token, id string) interface{}
- GetJoinNetworks() string
- NetworkIdFilter(letter rune) bool
- }
- type zerotierStruct struct {
- }
- var client http.Client
- func (c *zerotierStruct) ZeroTierJoinNetwork(networkId string) {
- command2.OnlyExec(`zerotier-cli join ` + networkId)
- }
- func (c *zerotierStruct) ZeroTierLeaveNetwork(networkId string) {
- command2.OnlyExec(`zerotier-cli leave ` + networkId)
- }
- //登录并获取token
- func (c *zerotierStruct) GetToken(username, pwd string) string {
- if len(config.ZeroTierInfo.Token) > 0 {
- return config.ZeroTierInfo.Token
- } else {
- return LoginGetToken(username, pwd)
- }
- }
- func (c *zerotierStruct) ZeroTierRegister(email, lastName, firstName, password string) string {
- url := "https://accounts.zerotier.com/auth/realms/zerotier/protocol/openid-connect/registrations?client_id=zt-central&redirect_uri=https%3A%2F%2Fmy.zerotier.com%2Fapi%2F_auth%2Foidc%2Fcallback&response_type=code&scope=openid+profile+email+offline_access&state=state"
- action, cookies, _ := ZeroTierGet(url, nil, 4)
- var buff bytes.Buffer
- buff.WriteString("email=")
- buff.WriteString(email)
- buff.WriteString("&password=")
- buff.WriteString(password)
- buff.WriteString("&password-confirm=")
- buff.WriteString(password)
- buff.WriteString("&user.attributes.marketingOptIn=true")
- buff.WriteString("&firstName")
- buff.WriteString(firstName)
- buff.WriteString("&lastName")
- buff.WriteString(lastName)
- action, errInfo, _ := ZeroTierPost(buff, action, cookies, false)
- if len(errInfo) > 0 {
- return errInfo
- }
- action, _, _ = ZeroTierGet(action, cookies, 5)
- return ""
- }
- //固定请求head
- func GetHead() map[string]string {
- var head = make(map[string]string, 4)
- head["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"
- head["Accept-Language"] = "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3"
- head["Connection"] = "keep-alive"
- head["User-Agent"] = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36"
- return head
- }
- //登录并获取token,会出现账号密码错误,和邮箱未验证情况,目前未出现其他情况
- func LoginGetToken(username, pwd string) string {
- //拿到登录的action
- var loginUrl = "https://accounts.zerotier.com/auth/realms/zerotier/protocol/openid-connect/auth?client_id=zt-central&redirect_uri=https%3A%2F%2Fmy.zerotier.com%2Fapi%2F_auth%2Foidc%2Fcallback&response_type=code&scope=openid+profile+email+offline_access&state=states"
- action, cookies, _ := ZeroTierGet(loginUrl, nil, 1)
- if len(action) == 0 {
- //没有拿到action,页面结构变了
- return ""
- }
- //登录
- var str bytes.Buffer
- str.WriteString("username=")
- str.WriteString(username)
- str.WriteString("&password=")
- str.WriteString(pwd)
- str.WriteString("&credentialId=&login=Log+In")
- url, logingErrInfo, _ := ZeroTierPost(str, action, cookies, true)
- action, cookies, isLoginOk := ZeroTierGet(url, cookies, 2)
- if isLoginOk {
- //登录成功,可以继续调用api
- randomTokenUrl := "https://my.zerotier.com/api/randomToken"
- json, _, _ := ZeroTierGet(randomTokenUrl, cookies, 3)
- //获取一个随机token
- token := gjson.Get(json, "token")
- userInfoUrl := "https://my.zerotier.com/api/status"
- json, _, _ = ZeroTierGet(userInfoUrl, cookies, 3)
- //拿到用户id
- userId := gjson.Get(json, "user.id")
- //设置新token
- addTokenUrl := "https://my.zerotier.com/api/user/" + userId.String() + "/token"
- data := make(map[string]string)
- rand.Seed(time.Now().UnixNano())
- data["tokenName"] = "oasis-token-" + strconv.Itoa(rand.Intn(1000))
- data["token"] = token.String()
- head := make(map[string]string)
- head["Content-Type"] = "application/json"
- _, statusCode := httper2.ZeroTierPost(addTokenUrl, data, head, cookies)
- if statusCode == http.StatusOK {
- config.Cfg.Section("zerotier").Key("Token").SetValue(token.String())
- config.Cfg.SaveTo("conf/conf.ini")
- config.ZeroTierInfo.Token = token.String()
- }
- } else {
- //登录错误信息
- if len(logingErrInfo) > 0 {
- return logingErrInfo
- } else {
- //验证邮箱
- action, _, _ = ZeroTierGet(url, cookies, 5)
- return "You need to verify your email address to activate your account."
- }
- }
- return ""
- }
- // t 1:获取action,2:登录成功后拿session(可能需要验证有了或登录失败) 3:随机生成token 4:注册页面拿action 5:注册成功后拿验证邮箱的地址
- func ZeroTierGet(url string, cookies []*http.Cookie, t uint8) (action string, c []*http.Cookie, isExistSession bool) {
- isExistSession = false
- action = ""
- c = []*http.Cookie{}
- request, _ := http.NewRequest(http.MethodGet, url, nil)
- for k, v := range GetHead() {
- request.Header.Add(k, v)
- }
- for _, cookie := range cookies {
- request.AddCookie(cookie)
- }
- resp, err := client.Do(request)
- if err != nil {
- return
- }
- defer resp.Body.Close()
- c = resp.Cookies()
- if t == 1 {
- doc, err := goquery.NewDocumentFromReader(resp.Body)
- if err != nil {
- return
- }
- action, _ = doc.Find("#kc-form-login").Attr("action")
- return
- } else if t == 2 {
- for _, cookie := range resp.Cookies() {
- if cookie.Name == "pgx-session" {
- isExistSession = true
- break
- }
- }
- //判断是否登录成功,如果需要验证邮箱,则返回验证邮箱的地址。
- if resp.StatusCode == http.StatusFound && len(resp.Header.Get("Location")) > 0 {
- action = resp.Header.Get("Location")
- }
- return
- } else if t == 3 {
- //返回获取到的字符串
- byteArr, _ := ioutil.ReadAll(resp.Body)
- action = string(byteArr)
- } else if t == 4 {
- doc, err := goquery.NewDocumentFromReader(resp.Body)
- if err != nil {
- return
- }
- action, _ = doc.Find("#kc-register-form").Attr("action")
- return
- } else if t == 5 {
- doc, _ := goquery.NewDocumentFromReader(resp.Body)
- fmt.Println(doc.Html())
- action, _ = doc.Find("#kc-info-wrapper a").Attr("href")
- return
- }
- return
- }
- //模拟提交表单
- func ZeroTierPost(str bytes.Buffer, action string, cookies []*http.Cookie, isLogin bool) (url, errInfo string, err error) {
- req, err := http.NewRequest(http.MethodPost, action, strings.NewReader(str.String()))
- if err != nil {
- return "", "", errors.New("newrequest error")
- }
- for k, v := range GetHead() {
- req.Header.Set(k, v)
- }
- req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
- for _, cookie := range cookies {
- req.AddCookie(cookie)
- }
- res, err := client.Do(req)
- defer res.Body.Close()
- if err != nil {
- return "", "", errors.New("request error")
- }
- if !isLogin {
- //注册成功
- if res.StatusCode == http.StatusFound && len(res.Header.Get("Location")) > 0 {
- return res.Header.Get("Location"), "", nil
- } else {
- register, _ := goquery.NewDocumentFromReader(res.Body)
- firstErr := strings.TrimSpace(register.Find("#input-error-firstname").Text())
- lastErr := strings.TrimSpace(register.Find("#input-error-lastname").Text())
- emailErr := strings.TrimSpace(register.Find("#input-error-email").Text())
- pwdErr := strings.TrimSpace(register.Find("#input-error-password").Text())
- var errD strings.Builder
- if len(firstErr) > 0 {
- errD.WriteString(firstErr + ",")
- }
- if len(lastErr) > 0 {
- errD.WriteString(lastErr + ",")
- }
- if len(emailErr) > 0 {
- errD.WriteString(emailErr + ",")
- }
- if len(pwdErr) > 0 {
- errD.WriteString(pwdErr + ",")
- }
- return "", errD.String(), nil
- }
- } else {
- if res.StatusCode == http.StatusFound && len(res.Header.Get("Location")) > 0 {
- return res.Header.Get("Location"), "", nil
- }
- doc, err := goquery.NewDocumentFromReader(res.Body)
- if err != nil {
- return "", "", errors.New("request error")
- }
- errDesc := doc.Find("#input-error").Text()
- if len(errDesc) > 0 {
- return "", strings.TrimSpace(errDesc), nil
- }
- }
- return "", "", nil
- }
- //获取zerotile网络列表和本地用户已加入的网络
- func (c *zerotierStruct) ZeroTierNetworkList(token string) (interface{}, []string) {
- url := "https://my.zerotier.com/api/network"
- return zerotier.GetData(url, token), command2.ExecResultStrArray(`zerotier-cli listnetworks | awk 'NR>1 {print $3} {line=$0}'`)
- }
- // get network info
- func (c *zerotierStruct) ZeroTierGetInfo(token, id string) (interface{}, []string) {
- url := "https://my.zerotier.com/api/network/" + id
- info := zerotier.GetData(url, token)
- return info, command2.ExecResultStrArray(`zerotier-cli listnetworks | awk 'NR>1 {print $3} {line=$0}'`)
- }
- //get status
- func (c *zerotierStruct) ZeroTierGetStatus(token string) interface{} {
- url := "https://my.zerotier.com/api/v1/status"
- info := zerotier.GetData(url, token)
- return info
- }
- func (c *zerotierStruct) EditNetwork(token string, data string, id string) interface{} {
- url := "https://my.zerotier.com/api/v1/network/" + id
- info := zerotier.PostData(url, token, data)
- return info
- }
- func (c *zerotierStruct) EditNetworkMember(token string, data string, id, mId string) interface{} {
- url := "https://my.zerotier.com/api/v1/network/" + id + "/member/" + mId
- info := zerotier.PostData(url, token, data)
- return info
- }
- func (c *zerotierStruct) MemberList(token string, id string) interface{} {
- url := "https://my.zerotier.com/api/v1/network/" + id + "/member"
- info := zerotier.GetData(url, token)
- return info
- }
- func (c *zerotierStruct) DeleteMember(token string, id, mId string) interface{} {
- url := "https://my.zerotier.com/api/v1/network/" + id + "/member/" + mId
- info := zerotier.DeleteMember(url, token)
- return info
- }
- func (c *zerotierStruct) DeleteNetwork(token, id string) interface{} {
- url := "https://my.zerotier.com/api/v1/network/" + id
- info := zerotier.DeleteMember(url, token)
- return info
- }
- func (c *zerotierStruct) CreateNetwork(token string) interface{} {
- url := "https://my.zerotier.com/api/v1/network"
- info := zerotier.PostData(url, token, "{}")
- return info
- }
- func (c *zerotierStruct) GetJoinNetworks() string {
- json := command2.ExecResultStr("source " + config.AppInfo.ProjectPath + "/shell/helper.sh ;GetLocalJoinNetworks")
- return json
- }
- func (c *zerotierStruct) NetworkIdFilter(letter rune) bool {
- if unicode.IsNumber(letter) || unicode.IsLetter(letter) {
- return true
- } else {
- return false
- }
- }
- func NewZeroTierService() ZeroTierService {
- //初始化client
- client = http.Client{Timeout: 30 * time.Second, CheckRedirect: func(req *http.Request, via []*http.Request) error {
- return http.ErrUseLastResponse //禁止重定向
- },
- }
- return &zerotierStruct{}
- }
|