http_client.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. package functions
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "log"
  8. "net/http"
  9. "net/url"
  10. "os"
  11. "os/signal"
  12. "strings"
  13. "github.com/gorilla/websocket"
  14. "github.com/gravitl/netmaker/cli/config"
  15. "github.com/gravitl/netmaker/logger"
  16. "github.com/gravitl/netmaker/models"
  17. )
  18. func ssoLogin(endpoint string) string {
  19. var (
  20. authToken string
  21. interrupt = make(chan os.Signal, 1)
  22. url, _ = url.Parse(endpoint)
  23. socketURL = fmt.Sprintf("wss://%s/api/oauth/headless", url.Host)
  24. )
  25. signal.Notify(interrupt, os.Interrupt)
  26. conn, _, err := websocket.DefaultDialer.Dial(socketURL, nil)
  27. if err != nil {
  28. log.Fatal("error connecting to endpoint ", socketURL, err.Error())
  29. }
  30. defer conn.Close()
  31. _, msg, err := conn.ReadMessage()
  32. if err != nil {
  33. log.Fatal("error reading from server: ", err.Error())
  34. }
  35. fmt.Printf("Please visit:\n %s \n to authenticate\n", string(msg))
  36. done := make(chan struct{})
  37. defer close(done)
  38. go func() {
  39. for {
  40. msgType, msg, err := conn.ReadMessage()
  41. if err != nil {
  42. if msgType < 0 {
  43. done <- struct{}{}
  44. return
  45. }
  46. if !strings.Contains(err.Error(), "normal") {
  47. log.Fatal("read error: ", err.Error())
  48. }
  49. return
  50. }
  51. if msgType == websocket.CloseMessage {
  52. done <- struct{}{}
  53. return
  54. }
  55. if strings.Contains(string(msg), "JWT: ") {
  56. authToken = strings.TrimPrefix(string(msg), "JWT: ")
  57. } else {
  58. logger.Log(0, "Message from server:", string(msg))
  59. return
  60. }
  61. }
  62. }()
  63. for {
  64. select {
  65. case <-done:
  66. return authToken
  67. case <-interrupt:
  68. err := conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
  69. if err != nil {
  70. logger.Log(0, "write close:", err.Error())
  71. }
  72. return authToken
  73. }
  74. }
  75. }
  76. func getAuthToken(ctx config.Context, force bool) string {
  77. if !force && ctx.AuthToken != "" {
  78. return ctx.AuthToken
  79. }
  80. if ctx.SSO {
  81. authToken := ssoLogin(ctx.Endpoint)
  82. config.SetAuthToken(authToken)
  83. return authToken
  84. }
  85. authParams := &models.UserAuthParams{UserName: ctx.Username, Password: ctx.Password}
  86. payload, err := json.Marshal(authParams)
  87. if err != nil {
  88. log.Fatal(err)
  89. }
  90. res, err := http.Post(ctx.Endpoint+"/api/users/adm/authenticate", "application/json", bytes.NewReader(payload))
  91. if err != nil {
  92. log.Fatal(err)
  93. }
  94. resBodyBytes, err := io.ReadAll(res.Body)
  95. if err != nil {
  96. log.Fatalf("Client could not read response body: %s", err)
  97. }
  98. if res.StatusCode != http.StatusOK {
  99. log.Fatalf("Error Status: %d Response: %s", res.StatusCode, string(resBodyBytes))
  100. }
  101. body := new(models.SuccessResponse)
  102. if err := json.Unmarshal(resBodyBytes, body); err != nil {
  103. log.Fatalf("Error unmarshalling JSON: %s", err)
  104. }
  105. authToken := body.Response.(map[string]any)["AuthToken"].(string)
  106. config.SetAuthToken(authToken)
  107. return authToken
  108. }
  109. func request[T any](method, route string, payload any) *T {
  110. var (
  111. _, ctx = config.GetCurrentContext()
  112. req *http.Request
  113. err error
  114. )
  115. if payload == nil {
  116. req, err = http.NewRequest(method, ctx.Endpoint+route, nil)
  117. if err != nil {
  118. log.Fatalf("Client could not create request: %s", err)
  119. }
  120. } else {
  121. payloadBytes, jsonErr := json.Marshal(payload)
  122. if jsonErr != nil {
  123. log.Fatalf("Error in request JSON marshalling: %s", err)
  124. }
  125. req, err = http.NewRequest(method, ctx.Endpoint+route, bytes.NewReader(payloadBytes))
  126. if err != nil {
  127. log.Fatalf("Client could not create request: %s", err)
  128. }
  129. req.Header.Set("Content-Type", "application/json")
  130. }
  131. if ctx.MasterKey != "" {
  132. req.Header.Set("Authorization", "Bearer "+ctx.MasterKey)
  133. } else {
  134. req.Header.Set("Authorization", "Bearer "+getAuthToken(ctx, false))
  135. }
  136. retried := false
  137. retry:
  138. res, err := http.DefaultClient.Do(req)
  139. if err != nil {
  140. log.Fatalf("Client error making http request: %s", err)
  141. }
  142. // refresh JWT token
  143. if res.StatusCode == http.StatusUnauthorized && !retried && ctx.MasterKey == "" {
  144. req.Header.Set("Authorization", "Bearer "+getAuthToken(ctx, true))
  145. retried = true
  146. goto retry
  147. }
  148. resBodyBytes, err := io.ReadAll(res.Body)
  149. if err != nil {
  150. log.Fatalf("Client could not read response body: %s", err)
  151. }
  152. if res.StatusCode != http.StatusOK {
  153. log.Fatalf("Error Status: %d Response: %s", res.StatusCode, string(resBodyBytes))
  154. }
  155. body := new(T)
  156. if len(resBodyBytes) > 0 {
  157. if err := json.Unmarshal(resBodyBytes, body); err != nil {
  158. log.Fatalf("Error unmarshalling JSON: %s", err)
  159. }
  160. }
  161. return body
  162. }
  163. func get(route string) string {
  164. _, ctx := config.GetCurrentContext()
  165. req, err := http.NewRequest(http.MethodGet, ctx.Endpoint+route, nil)
  166. if err != nil {
  167. log.Fatal(err)
  168. }
  169. if ctx.MasterKey != "" {
  170. req.Header.Set("Authorization", "Bearer "+ctx.MasterKey)
  171. } else {
  172. req.Header.Set("Authorization", "Bearer "+getAuthToken(ctx, true))
  173. }
  174. res, err := http.DefaultClient.Do(req)
  175. if err != nil {
  176. log.Fatal(err)
  177. }
  178. bodyBytes, err := io.ReadAll(res.Body)
  179. if err != nil {
  180. log.Fatal(err)
  181. }
  182. return string(bodyBytes)
  183. }