api_test.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. /*
  2. Copyright © The ESO Authors
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. https://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package api
  14. import (
  15. "errors"
  16. "fmt"
  17. "regexp"
  18. "strconv"
  19. "testing"
  20. infisical "github.com/infisical/go-sdk"
  21. "github.com/stretchr/testify/assert"
  22. )
  23. func parseInfisicalAPIError(err error, t *testing.T) (int, string, error) {
  24. var apiErr *infisical.APIError
  25. assert.True(t, errors.As(err, &apiErr))
  26. // Regex to extract status-code
  27. statusRegex := regexp.MustCompile(`\[status-code=(\d+)\]`)
  28. statusMatch := statusRegex.FindStringSubmatch(apiErr.Error())
  29. // Regex to extract message (handles quoted content)
  30. messageRegex := regexp.MustCompile(`\[message="([^"]*)"\]`)
  31. messageMatch := messageRegex.FindStringSubmatch(apiErr.Error())
  32. if len(statusMatch) < 2 {
  33. return 0, "", fmt.Errorf("status-code not found in error string")
  34. }
  35. if len(messageMatch) < 2 {
  36. return 0, "", fmt.Errorf("message not found in error string")
  37. }
  38. statusCode, err := strconv.Atoi(statusMatch[1])
  39. if err != nil {
  40. return 0, "", fmt.Errorf("invalid status code: %w", err)
  41. }
  42. return statusCode, messageMatch[1], nil
  43. }
  44. const errNoAccessToken = "sdk client is not authenticated, cannot revoke access token"
  45. const (
  46. fakeClientID = "client-id"
  47. fakeClientSecret = "client-secret"
  48. fakeToken = "token"
  49. fakeProjectSlug = "first-project"
  50. fakeEnvironmentSlug = "dev"
  51. )
  52. func TestSetTokenViaMachineIdentity(t *testing.T) {
  53. t.Run("Success", func(t *testing.T) {
  54. apiClient, closeFunc := NewMockClient(200, MachineIdentityDetailsResponse{
  55. AccessToken: "foobar",
  56. ExpiresIn: 2592000,
  57. AccessTokenMaxTTL: 2592000,
  58. TokenType: "Bearer",
  59. })
  60. defer closeFunc()
  61. _, err := apiClient.Auth().UniversalAuthLogin(fakeClientID, fakeClientSecret)
  62. assert.NoError(t, err)
  63. assert.Equal(t, apiClient.Auth().GetAccessToken(), "foobar")
  64. })
  65. t.Run("SetTokenViaMachineIdentity: Error when non-200 response received", func(t *testing.T) {
  66. apiClient, closeFunc := NewMockClient(401, InfisicalAPIErrorResponse{
  67. StatusCode: 401,
  68. Message: "Unauthorized",
  69. })
  70. defer closeFunc()
  71. _, err := apiClient.Auth().UniversalAuthLogin(fakeClientID, fakeClientSecret)
  72. assert.Error(t, err)
  73. apiErrorStatusCode, apiErrorMessage, err := parseInfisicalAPIError(err, t)
  74. if err != nil {
  75. t.Fatalf("Error parsing infisical API error: %v", err)
  76. }
  77. assert.Equal(t, 401, apiErrorStatusCode)
  78. assert.Equal(t, "Unauthorized", apiErrorMessage)
  79. })
  80. }
  81. func TestRevokeAccessToken(t *testing.T) {
  82. t.Run("Success", func(t *testing.T) {
  83. apiClient, closeFunc := NewMockClient(200, RevokeMachineIdentityAccessTokenResponse{
  84. Message: "Success",
  85. })
  86. defer closeFunc()
  87. apiClient.Auth().SetAccessToken(fakeToken)
  88. err := apiClient.Auth().RevokeAccessToken()
  89. assert.NoError(t, err)
  90. // Verify that the access token was unset.
  91. assert.Equal(t, apiClient.Auth().GetAccessToken(), "")
  92. })
  93. t.Run("RevokeAccessToken: Error when non-200 response received", func(t *testing.T) {
  94. apiClient, closeFunc := NewMockClient(401, InfisicalAPIErrorResponse{
  95. StatusCode: 401,
  96. Message: "Unauthorized",
  97. })
  98. defer closeFunc()
  99. apiClient.Auth().SetAccessToken(fakeToken)
  100. err := apiClient.Auth().RevokeAccessToken()
  101. assert.Error(t, err)
  102. apiErrorStatusCode, apiErrorMessage, err := parseInfisicalAPIError(err, t)
  103. if err != nil {
  104. t.Fatalf("Error parsing infisical API error: %v", err)
  105. }
  106. assert.Equal(t, 401, apiErrorStatusCode)
  107. assert.Equal(t, "Unauthorized", apiErrorMessage)
  108. })
  109. t.Run("Error when no access token is set", func(t *testing.T) {
  110. apiClient, closeFunc := NewMockClient(401, nil)
  111. defer closeFunc()
  112. err := apiClient.Auth().RevokeAccessToken()
  113. assert.EqualError(t, err, errNoAccessToken)
  114. })
  115. }
  116. func TestGetSecretsV3(t *testing.T) {
  117. t.Run("Works with secrets", func(t *testing.T) {
  118. secrets := []SecretsV3{
  119. {SecretKey: "foo", SecretValue: "bar"},
  120. }
  121. apiClient, closeFunc := NewMockClient(200, GetSecretsV3Response{
  122. Secrets: secrets,
  123. })
  124. sdkFormattedSecrets := make([]infisical.Secret, 0, len(secrets))
  125. for _, secret := range secrets {
  126. sdkFormattedSecrets = append(sdkFormattedSecrets, infisical.Secret{
  127. SecretKey: secret.SecretKey,
  128. SecretValue: secret.SecretValue,
  129. })
  130. }
  131. defer closeFunc()
  132. sdkSecrets, err := apiClient.Secrets().List(infisical.ListSecretsOptions{
  133. ProjectSlug: fakeProjectSlug,
  134. Environment: fakeEnvironmentSlug,
  135. SecretPath: "/",
  136. Recursive: true,
  137. })
  138. assert.NoError(t, err)
  139. assert.Equal(t, sdkSecrets, sdkFormattedSecrets)
  140. })
  141. t.Run("Works with imported secrets", func(t *testing.T) {
  142. secrets := []SecretsV3{
  143. {SecretKey: "foo", SecretValue: "bar"},
  144. }
  145. apiClient, closeFunc := NewMockClient(200, GetSecretsV3Response{
  146. ImportedSecrets: []ImportedSecretV3{{
  147. Secrets: secrets,
  148. }},
  149. })
  150. defer closeFunc()
  151. sdkFormattedSecrets := make([]infisical.Secret, 0, len(secrets))
  152. for _, secret := range secrets {
  153. sdkFormattedSecrets = append(sdkFormattedSecrets, infisical.Secret{
  154. SecretKey: secret.SecretKey,
  155. SecretValue: secret.SecretValue,
  156. })
  157. }
  158. sdkSecrets, err := apiClient.Secrets().List(infisical.ListSecretsOptions{
  159. ProjectSlug: fakeProjectSlug,
  160. Environment: fakeEnvironmentSlug,
  161. IncludeImports: true,
  162. SecretPath: "/",
  163. Recursive: true,
  164. })
  165. assert.NoError(t, err)
  166. assert.Equal(t, sdkSecrets, sdkFormattedSecrets)
  167. })
  168. t.Run("GetSecretsV3: Error when non-200 response received", func(t *testing.T) {
  169. apiClient, closeFunc := NewMockClient(401, InfisicalAPIErrorResponse{
  170. StatusCode: 401,
  171. Message: "Unauthorized",
  172. })
  173. defer closeFunc()
  174. _, err := apiClient.Secrets().List(infisical.ListSecretsOptions{
  175. ProjectSlug: fakeProjectSlug,
  176. Environment: fakeEnvironmentSlug,
  177. SecretPath: "/",
  178. Recursive: true,
  179. })
  180. assert.Error(t, err)
  181. apiErrorStatusCode, apiErrorMessage, err := parseInfisicalAPIError(err, t)
  182. if err != nil {
  183. t.Fatalf("Error parsing infisical API error: %v", err)
  184. }
  185. assert.Equal(t, 401, apiErrorStatusCode)
  186. assert.Equal(t, "Unauthorized", apiErrorMessage)
  187. })
  188. }
  189. func TestGetSecretByKeyV3(t *testing.T) {
  190. t.Run("Works", func(t *testing.T) {
  191. secret := SecretsV3{
  192. SecretKey: "foo",
  193. SecretValue: "bar",
  194. }
  195. sdkFormattedSecret := infisical.Secret{
  196. SecretKey: secret.SecretKey,
  197. SecretValue: secret.SecretValue,
  198. }
  199. apiClient, closeFunc := NewMockClient(200, GetSecretByKeyV3Response{
  200. Secret: secret,
  201. })
  202. defer closeFunc()
  203. sdkSecret, err := apiClient.Secrets().Retrieve(infisical.RetrieveSecretOptions{
  204. ProjectSlug: fakeProjectSlug,
  205. Environment: fakeEnvironmentSlug,
  206. SecretPath: "/",
  207. IncludeImports: true,
  208. SecretKey: "foo",
  209. })
  210. assert.NoError(t, err)
  211. assert.Equal(t, sdkSecret, sdkFormattedSecret)
  212. })
  213. t.Run("Error when secret is not found", func(t *testing.T) {
  214. apiClient, closeFunc := NewMockClient(404, InfisicalAPIErrorResponse{
  215. StatusCode: 404,
  216. Message: "Not Found",
  217. })
  218. defer closeFunc()
  219. _, err := apiClient.Secrets().Retrieve(infisical.RetrieveSecretOptions{
  220. ProjectSlug: fakeProjectSlug,
  221. Environment: fakeEnvironmentSlug,
  222. SecretPath: "/",
  223. IncludeImports: true,
  224. SecretKey: "foo",
  225. })
  226. assert.Error(t, err)
  227. apiErrorStatusCode, apiErrorMessage, err := parseInfisicalAPIError(err, t)
  228. if err != nil {
  229. t.Fatalf("Error parsing infisical API error: %v", err)
  230. }
  231. assert.Equal(t, 404, apiErrorStatusCode)
  232. assert.Equal(t, "Not Found", apiErrorMessage)
  233. })
  234. // Test case where the request is unauthorized
  235. t.Run("ErrorHandlingUnauthorized", func(t *testing.T) {
  236. apiClient, closeFunc := NewMockClient(401, InfisicalAPIErrorResponse{
  237. StatusCode: 401,
  238. Message: "Unauthorized",
  239. })
  240. defer closeFunc()
  241. _, err := apiClient.Secrets().Retrieve(infisical.RetrieveSecretOptions{
  242. ProjectSlug: fakeProjectSlug,
  243. Environment: fakeEnvironmentSlug,
  244. SecretPath: "/",
  245. IncludeImports: true,
  246. SecretKey: "foo",
  247. })
  248. assert.Error(t, err)
  249. apiErrorStatusCode, apiErrorMessage, err := parseInfisicalAPIError(err, t)
  250. if err != nil {
  251. t.Fatalf("Error parsing infisical API error: %v", err)
  252. }
  253. assert.Equal(t, 401, apiErrorStatusCode)
  254. assert.Equal(t, "Unauthorized", apiErrorMessage)
  255. })
  256. }