http_client.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  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", 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. // Access was granted
  57. authToken = strings.TrimPrefix(string(msg), "JWT: ")
  58. } else {
  59. logger.Log(0, "Message from server:", string(msg))
  60. return
  61. }
  62. }
  63. }()
  64. for {
  65. select {
  66. case <-done:
  67. return authToken
  68. case <-interrupt:
  69. err := conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
  70. if err != nil {
  71. logger.Log(0, "write close:", err.Error())
  72. }
  73. return authToken
  74. }
  75. }
  76. }
  77. func getAuthToken(ctx config.Context, force bool) string {
  78. if !force && ctx.AuthToken != "" {
  79. return ctx.AuthToken
  80. }
  81. if ctx.SSO {
  82. authToken := ssoLogin(ctx.Endpoint)
  83. config.SetAuthToken(authToken)
  84. return authToken
  85. }
  86. authParams := &models.UserAuthParams{UserName: ctx.Username, Password: ctx.Password}
  87. payload, err := json.Marshal(authParams)
  88. if err != nil {
  89. log.Fatal(err)
  90. }
  91. res, err := http.Post(ctx.Endpoint+"/api/users/adm/authenticate", "application/json", bytes.NewReader(payload))
  92. if err != nil {
  93. log.Fatal(err)
  94. }
  95. resBodyBytes, err := io.ReadAll(res.Body)
  96. if err != nil {
  97. log.Fatalf("Client could not read response body: %s", err)
  98. }
  99. if res.StatusCode != http.StatusOK {
  100. log.Fatalf("Error Status: %d Response: %s", res.StatusCode, string(resBodyBytes))
  101. }
  102. body := new(models.SuccessResponse)
  103. if err := json.Unmarshal(resBodyBytes, body); err != nil {
  104. log.Fatalf("Error unmarshalling JSON: %s", err)
  105. }
  106. authToken := body.Response.(map[string]any)["AuthToken"].(string)
  107. config.SetAuthToken(authToken)
  108. return authToken
  109. }
  110. func request[T any](method, route string, payload any) *T {
  111. var (
  112. _, ctx = config.GetCurrentContext()
  113. req *http.Request
  114. err error
  115. )
  116. if payload == nil {
  117. req, err = http.NewRequest(method, ctx.Endpoint+route, nil)
  118. if err != nil {
  119. log.Fatalf("Client could not create request: %s", err)
  120. }
  121. } else {
  122. payloadBytes, jsonErr := json.Marshal(payload)
  123. if jsonErr != nil {
  124. log.Fatalf("Error in request JSON marshalling: %s", err)
  125. }
  126. req, err = http.NewRequest(method, ctx.Endpoint+route, bytes.NewReader(payloadBytes))
  127. if err != nil {
  128. log.Fatalf("Client could not create request: %s", err)
  129. }
  130. req.Header.Set("Content-Type", "application/json")
  131. }
  132. if ctx.MasterKey != "" {
  133. req.Header.Set("Authorization", "Bearer "+ctx.MasterKey)
  134. } else {
  135. req.Header.Set("Authorization", "Bearer "+getAuthToken(ctx, false))
  136. }
  137. retried := false
  138. retry:
  139. res, err := http.DefaultClient.Do(req)
  140. if err != nil {
  141. log.Fatalf("Client error making http request: %s", err)
  142. }
  143. // refresh JWT token
  144. if res.StatusCode == http.StatusUnauthorized && !retried && ctx.MasterKey == "" {
  145. req.Header.Set("Authorization", "Bearer "+getAuthToken(ctx, true))
  146. retried = true
  147. goto retry
  148. }
  149. resBodyBytes, err := io.ReadAll(res.Body)
  150. if err != nil {
  151. log.Fatalf("Client could not read response body: %s", err)
  152. }
  153. if res.StatusCode != http.StatusOK {
  154. log.Fatalf("Error Status: %d Response: %s", res.StatusCode, string(resBodyBytes))
  155. }
  156. body := new(T)
  157. if len(resBodyBytes) > 0 {
  158. if err := json.Unmarshal(resBodyBytes, body); err != nil {
  159. log.Fatalf("Error unmarshalling JSON: %s", err)
  160. }
  161. }
  162. return body
  163. }
  164. func get(route string) string {
  165. _, ctx := config.GetCurrentContext()
  166. req, err := http.NewRequest(http.MethodGet, ctx.Endpoint+route, nil)
  167. if err != nil {
  168. log.Fatal(err)
  169. }
  170. if ctx.MasterKey != "" {
  171. req.Header.Set("Authorization", "Bearer "+ctx.MasterKey)
  172. } else {
  173. req.Header.Set("Authorization", "Bearer "+getAuthToken(ctx, true))
  174. }
  175. res, err := http.DefaultClient.Do(req)
  176. if err != nil {
  177. log.Fatal(err)
  178. }
  179. bodyBytes, err := io.ReadAll(res.Body)
  180. if err != nil {
  181. log.Fatal(err)
  182. }
  183. return string(bodyBytes)
  184. }