// Package sso 群辉登录 package sso import ( "bytes" "encoding/json" "errors" "fmt" "log" "net/url" "os" "os/exec" "regexp" "strings" ) // AppPrivilege is part of AuthJSON type AppPrivilege struct { IsPermitted bool `json:"SYNO.SDS.DNSCryptProxy.Application"` } // Session is part of AuthJSON type Session struct { IsAdmin bool `json:"is_admin"` } // AuthJSON is used to read JSON data from /usr/syno/synoman/webman/initdata.cgi type AuthJSON struct { Session Session `json:"session"` AppPrivilege AppPrivilege } type RespJSON struct { Code int `json:"code"` Msg string `json:"msg"` Data any `json:"data"` } type UserData struct { User string `json:"user"` SynoToken string `json:"synoToken"` Data string `json:"data"` } type SynoResp struct { StatusCode int Message string Data UserData Ok bool } func (r *SynoResp) PrintNoData() { fmt.Print(r.Message) } func OkResp() SynoResp { return SynoResp{ StatusCode: 200, Message: "Status: 200 OK\nContent-Type: application/json; charset=utf-8\n\n", Ok: true, } } func UnauthorisedResp() SynoResp { return SynoResp{ StatusCode: 401, Message: "Status: 401 Unauthorized\nContent-Type: text/html; charset=utf-8\n\n", Ok: false, } } func ErrorResp() SynoResp { return SynoResp{ StatusCode: 500, Message: "Status: 500 Internal server error\nContent-Type: text/html; charset=utf-8\n\n", Ok: false, } } // Retrieve login status and try to retrieve a CSRF token. // If either fails than we return an error to the user that they need to login. // Returns username or error // 现在没有权限访问这个,需要客户端进行调用 func token() (string, error) { cmd := exec.Command("/usr/syno/synoman/webman/login.cgi") cmdOut, err := cmd.Output() if err != nil && err.Error() != "exit status 255" { // in the Synology world, error code 255 apparently means success! return string(cmdOut), err } // cmdOut = bytes.TrimLeftFunc(cmdOut, findJSON) // Content-Type: text/html [..] { "SynoToken" : "GqHdJil0ZmlhE", "result" : "success", "success" : true } r, err := regexp.Compile("SynoToken\" *: *\"([^\"]+)\"") if err != nil { return string(cmdOut), err } token := r.FindSubmatch(cmdOut) if len(token) < 1 { return string(cmdOut), errors.New("Sorry, you need to login first!") } return string(token[1]), nil } // Detect if the rune (character) contains '{' and therefore is likely to contain JSON // returns bool func findJSON(r rune) bool { if r == '{' { return false } return true } // Check if the logged in user is Authorised or Admin. // If either fails than we return a HTTP Unauthorized error. func SynoAuth() SynoResp { //to, err := token() //if err != nil { // log.Println("getToken fail", err) // return UnauthorisedResp() //} else { // log.Println("getToken ok...", to) //} // X-SYNO-TOKEN:9WuK4Cf50Vw7Q // http://192.168.1.1:5000/webman/3rdparty/DownloadStation/webUI/downloadman.cgi?SynoToken=9WuK4Cf50Vw7Q tempQueryEnv := os.Getenv("QUERY_STRING") log.Println("tempQueryEnv: ", tempQueryEnv) if tempQueryEnv == "" || !strings.Contains(tempQueryEnv, "SynoToken") { log.Println("query str miss SynoToken") return ErrorResp() } u, err := url.Parse(fmt.Sprintf("http://test.example.cn/?%s", tempQueryEnv)) if err != nil { log.Println("parse query params fail: ", err) return ErrorResp() } query := u.Query() tokenStr := query.Get("SynoToken") log.Println("tokenStr: ", tokenStr) //os.Setenv("QUERY_STRING", "SynoToken="+to) cmd := exec.Command("/usr/syno/synoman/webman/modules/authenticate.cgi") user, err := cmd.Output() if err != nil && string(user) == "" { log.Println("authenticate fail", err) return UnauthorisedResp() } log.Println("user: ", string(user)) account := strings.NewReplacer("\r", "").Replace(string(user)) account = strings.NewReplacer("\n", "").Replace(account) var userData = UserData{ User: account, SynoToken: tokenStr, } // check permissions if checkIfFileExists("/usr/syno/synoman/webman/initdata.cgi") { cmd = exec.Command("/usr/syno/synoman/webman/initdata.cgi") // performance hit cmdOut, err := cmd.Output() if err != nil { log.Println("initdata fail", err) //unauthorised() } cmdOut = bytes.TrimLeftFunc(cmdOut, findJSON) var jsonData AuthJSON if err := json.Unmarshal(cmdOut, &jsonData); err != nil { // performance hit log.Println("initdata Unmarshal fail", err) //unauthorised() } isAdmin := jsonData.Session.IsAdmin // Session.IsAdmin:true isPermitted := jsonData.AppPrivilege.IsPermitted // AppPrivilege.SYNO.SDS.DNSCryptProxy.Application:true if !(isAdmin || isPermitted) { log.Println("initdata Unmarshal fail", err) //unauthorised() } } //os.Setenv("QUERY_STRING", tempQueryEnv) resp := OkResp() resp.Data = userData return resp } // Return true if the file path exists. func checkIfFileExists(file string) bool { _, err := os.Stat(file) if err != nil { if os.IsNotExist(err) { return false } log.Printf("checkIfFileExists error: %v", err) } return true }