http_client.go 5.1 KB

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