syno.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. // Package sso 群辉登录
  2. package sso
  3. import (
  4. "bytes"
  5. "encoding/json"
  6. "errors"
  7. "fmt"
  8. "log"
  9. "net/url"
  10. "os"
  11. "os/exec"
  12. "regexp"
  13. "strings"
  14. )
  15. // AppPrivilege is part of AuthJSON
  16. type AppPrivilege struct {
  17. IsPermitted bool `json:"SYNO.SDS.DNSCryptProxy.Application"`
  18. }
  19. // Session is part of AuthJSON
  20. type Session struct {
  21. IsAdmin bool `json:"is_admin"`
  22. }
  23. // AuthJSON is used to read JSON data from /usr/syno/synoman/webman/initdata.cgi
  24. type AuthJSON struct {
  25. Session Session `json:"session"`
  26. AppPrivilege AppPrivilege
  27. }
  28. type RespJSON struct {
  29. Code int `json:"code"`
  30. Msg string `json:"msg"`
  31. Data any `json:"data"`
  32. }
  33. type UserData struct {
  34. User string `json:"user"`
  35. SynoToken string `json:"synoToken"`
  36. Data string `json:"data"`
  37. }
  38. type SynoResp struct {
  39. StatusCode int
  40. Message string
  41. Data UserData
  42. Ok bool
  43. }
  44. func (r *SynoResp) PrintNoData() {
  45. fmt.Print(r.Message)
  46. }
  47. func OkResp() SynoResp {
  48. return SynoResp{
  49. StatusCode: 200,
  50. Message: "Status: 200 OK\nContent-Type: application/json; charset=utf-8\n\n",
  51. Ok: true,
  52. }
  53. }
  54. func UnauthorisedResp() SynoResp {
  55. return SynoResp{
  56. StatusCode: 401,
  57. Message: "Status: 401 Unauthorized\nContent-Type: text/html; charset=utf-8\n\n",
  58. Ok: false,
  59. }
  60. }
  61. func ErrorResp() SynoResp {
  62. return SynoResp{
  63. StatusCode: 500,
  64. Message: "Status: 500 Internal server error\nContent-Type: text/html; charset=utf-8\n\n",
  65. Ok: false,
  66. }
  67. }
  68. // Retrieve login status and try to retrieve a CSRF token.
  69. // If either fails than we return an error to the user that they need to login.
  70. // Returns username or error
  71. // 现在没有权限访问这个,需要客户端进行调用
  72. func token() (string, error) {
  73. cmd := exec.Command("/usr/syno/synoman/webman/login.cgi")
  74. cmdOut, err := cmd.Output()
  75. if err != nil && err.Error() != "exit status 255" { // in the Synology world, error code 255 apparently means success!
  76. return string(cmdOut), err
  77. }
  78. // cmdOut = bytes.TrimLeftFunc(cmdOut, findJSON)
  79. // Content-Type: text/html [..] { "SynoToken" : "GqHdJil0ZmlhE", "result" : "success", "success" : true }
  80. r, err := regexp.Compile("SynoToken\" *: *\"([^\"]+)\"")
  81. if err != nil {
  82. return string(cmdOut), err
  83. }
  84. token := r.FindSubmatch(cmdOut)
  85. if len(token) < 1 {
  86. return string(cmdOut), errors.New("Sorry, you need to login first!")
  87. }
  88. return string(token[1]), nil
  89. }
  90. // Detect if the rune (character) contains '{' and therefore is likely to contain JSON
  91. // returns bool
  92. func findJSON(r rune) bool {
  93. if r == '{' {
  94. return false
  95. }
  96. return true
  97. }
  98. // Check if the logged in user is Authorised or Admin.
  99. // If either fails than we return a HTTP Unauthorized error.
  100. func SynoAuth() SynoResp {
  101. //to, err := token()
  102. //if err != nil {
  103. // log.Println("getToken fail", err)
  104. // return UnauthorisedResp()
  105. //} else {
  106. // log.Println("getToken ok...", to)
  107. //}
  108. // X-SYNO-TOKEN:9WuK4Cf50Vw7Q
  109. // http://192.168.1.1:5000/webman/3rdparty/DownloadStation/webUI/downloadman.cgi?SynoToken=9WuK4Cf50Vw7Q
  110. tempQueryEnv := os.Getenv("QUERY_STRING")
  111. log.Println("tempQueryEnv: ", tempQueryEnv)
  112. if tempQueryEnv == "" || !strings.Contains(tempQueryEnv, "SynoToken") {
  113. log.Println("query str miss SynoToken")
  114. return ErrorResp()
  115. }
  116. u, err := url.Parse(fmt.Sprintf("http://test.example.cn/?%s", tempQueryEnv))
  117. if err != nil {
  118. log.Println("parse query params fail: ", err)
  119. return ErrorResp()
  120. }
  121. query := u.Query()
  122. tokenStr := query.Get("SynoToken")
  123. log.Println("tokenStr: ", tokenStr)
  124. //os.Setenv("QUERY_STRING", "SynoToken="+to)
  125. cmd := exec.Command("/usr/syno/synoman/webman/modules/authenticate.cgi")
  126. user, err := cmd.Output()
  127. if err != nil && string(user) == "" {
  128. log.Println("authenticate fail", err)
  129. return UnauthorisedResp()
  130. }
  131. log.Println("user: ", string(user))
  132. account := strings.NewReplacer("\r", "").Replace(string(user))
  133. account = strings.NewReplacer("\n", "").Replace(account)
  134. var userData = UserData{
  135. User: account,
  136. SynoToken: tokenStr,
  137. }
  138. // check permissions
  139. if checkIfFileExists("/usr/syno/synoman/webman/initdata.cgi") {
  140. cmd = exec.Command("/usr/syno/synoman/webman/initdata.cgi") // performance hit
  141. cmdOut, err := cmd.Output()
  142. if err != nil {
  143. log.Println("initdata fail", err)
  144. //unauthorised()
  145. }
  146. cmdOut = bytes.TrimLeftFunc(cmdOut, findJSON)
  147. var jsonData AuthJSON
  148. if err := json.Unmarshal(cmdOut, &jsonData); err != nil { // performance hit
  149. log.Println("initdata Unmarshal fail", err)
  150. //unauthorised()
  151. }
  152. isAdmin := jsonData.Session.IsAdmin // Session.IsAdmin:true
  153. isPermitted := jsonData.AppPrivilege.IsPermitted // AppPrivilege.SYNO.SDS.DNSCryptProxy.Application:true
  154. if !(isAdmin || isPermitted) {
  155. log.Println("initdata Unmarshal fail", err)
  156. //unauthorised()
  157. }
  158. }
  159. //os.Setenv("QUERY_STRING", tempQueryEnv)
  160. resp := OkResp()
  161. resp.Data = userData
  162. return resp
  163. }
  164. // Return true if the file path exists.
  165. func checkIfFileExists(file string) bool {
  166. _, err := os.Stat(file)
  167. if err != nil {
  168. if os.IsNotExist(err) {
  169. return false
  170. }
  171. log.Printf("checkIfFileExists error: %v", err)
  172. }
  173. return true
  174. }