kms_test.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. /*
  2. Copyright © 2025 ESO Maintainer Team
  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 alibaba
  14. import (
  15. "context"
  16. "errors"
  17. "reflect"
  18. "strings"
  19. "testing"
  20. kmssdk "github.com/alibabacloud-go/kms-20160120/v3/client"
  21. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  22. esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
  23. fakesm "github.com/external-secrets/external-secrets/providers/v1/alibaba/fake"
  24. "github.com/external-secrets/external-secrets/runtime/esutils"
  25. )
  26. const (
  27. secretName = "test-example"
  28. secretValue = "value"
  29. )
  30. type keyManagementServiceTestCase struct {
  31. mockClient *fakesm.AlibabaMockClient
  32. apiInput *kmssdk.GetSecretValueRequest
  33. apiOutput *kmssdk.GetSecretValueResponseBody
  34. ref *esv1.ExternalSecretDataRemoteRef
  35. apiErr error
  36. expectError string
  37. expectedSecret string
  38. // for testing secretmap
  39. expectedData map[string][]byte
  40. }
  41. func makeValidKMSTestCase() *keyManagementServiceTestCase {
  42. kmstc := keyManagementServiceTestCase{
  43. mockClient: &fakesm.AlibabaMockClient{},
  44. apiInput: makeValidAPIInput(),
  45. ref: makeValidRef(),
  46. apiOutput: makeValidAPIOutput(),
  47. apiErr: nil,
  48. expectError: "",
  49. expectedSecret: "",
  50. expectedData: make(map[string][]byte),
  51. }
  52. kmstc.mockClient.WithValue(kmstc.apiInput, kmstc.apiOutput, kmstc.apiErr)
  53. return &kmstc
  54. }
  55. func makeValidRef() *esv1.ExternalSecretDataRemoteRef {
  56. return &esv1.ExternalSecretDataRemoteRef{
  57. Key: secretName,
  58. }
  59. }
  60. func makeValidAPIInput() *kmssdk.GetSecretValueRequest {
  61. return &kmssdk.GetSecretValueRequest{
  62. SecretName: esutils.Ptr(secretName),
  63. }
  64. }
  65. func makeValidAPIOutput() *kmssdk.GetSecretValueResponseBody {
  66. response := &kmssdk.GetSecretValueResponseBody{
  67. SecretName: esutils.Ptr(secretName),
  68. SecretData: esutils.Ptr(secretValue),
  69. VersionStages: &kmssdk.GetSecretValueResponseBodyVersionStages{},
  70. }
  71. return response
  72. }
  73. func makeValidKMSTestCaseCustom(tweaks ...func(kmstc *keyManagementServiceTestCase)) *keyManagementServiceTestCase {
  74. kmstc := makeValidKMSTestCase()
  75. for _, fn := range tweaks {
  76. fn(kmstc)
  77. }
  78. kmstc.mockClient.WithValue(kmstc.apiInput, kmstc.apiOutput, kmstc.apiErr)
  79. return kmstc
  80. }
  81. var setAPIErr = func(kmstc *keyManagementServiceTestCase) {
  82. kmstc.apiErr = errors.New("oh no")
  83. kmstc.expectError = "oh no"
  84. }
  85. var setNilMockClient = func(kmstc *keyManagementServiceTestCase) {
  86. kmstc.mockClient = nil
  87. kmstc.expectError = errUninitalizedAlibabaProvider
  88. }
  89. func TestAlibabaKMSGetSecret(t *testing.T) {
  90. secretData := make(map[string]any)
  91. secretValue := "changedvalue"
  92. secretData["payload"] = secretValue
  93. // good case: default version is set
  94. // key is passed in, output is sent back
  95. setSecretString := func(kmstc *keyManagementServiceTestCase) {
  96. kmstc.apiOutput.SecretName = esutils.Ptr(secretName)
  97. kmstc.apiOutput.SecretData = esutils.Ptr(secretValue)
  98. kmstc.expectedSecret = secretValue
  99. }
  100. // good case: custom version set
  101. setCustomKey := func(kmstc *keyManagementServiceTestCase) {
  102. kmstc.apiOutput.SecretName = esutils.Ptr("test-example-other")
  103. kmstc.ref.Key = "test-example-other"
  104. kmstc.apiOutput.SecretData = esutils.Ptr(secretValue)
  105. kmstc.expectedSecret = secretValue
  106. }
  107. successCases := []*keyManagementServiceTestCase{
  108. makeValidKMSTestCaseCustom(setSecretString),
  109. makeValidKMSTestCaseCustom(setCustomKey),
  110. makeValidKMSTestCaseCustom(setAPIErr),
  111. makeValidKMSTestCaseCustom(setNilMockClient),
  112. }
  113. sm := KeyManagementService{}
  114. for k, v := range successCases {
  115. sm.Client = v.mockClient
  116. out, err := sm.GetSecret(context.Background(), *v.ref)
  117. if !ErrorContains(err, v.expectError) {
  118. t.Errorf("[%d] unexpected error: %s, expected: '%s'", k, err.Error(), v.expectError)
  119. }
  120. if string(out) != v.expectedSecret {
  121. t.Errorf("[%d] unexpected secret: expected %s, got %s", k, v.expectedSecret, string(out))
  122. }
  123. }
  124. }
  125. func TestGetSecretMap(t *testing.T) {
  126. // good case: default version & deserialization
  127. setDeserialization := func(kmstc *keyManagementServiceTestCase) {
  128. kmstc.apiOutput.SecretName = esutils.Ptr("foo")
  129. kmstc.expectedData["foo"] = []byte("bar")
  130. kmstc.apiOutput.SecretData = esutils.Ptr(`{"foo":"bar"}`)
  131. }
  132. // bad case: invalid json
  133. setInvalidJSON := func(kmstc *keyManagementServiceTestCase) {
  134. kmstc.apiOutput.SecretData = esutils.Ptr("-----------------")
  135. kmstc.expectError = "unable to unmarshal secret"
  136. }
  137. successCases := []*keyManagementServiceTestCase{
  138. makeValidKMSTestCaseCustom(setDeserialization),
  139. makeValidKMSTestCaseCustom(setInvalidJSON),
  140. makeValidKMSTestCaseCustom(setNilMockClient),
  141. makeValidKMSTestCaseCustom(setAPIErr),
  142. }
  143. sm := KeyManagementService{}
  144. for k, v := range successCases {
  145. sm.Client = v.mockClient
  146. out, err := sm.GetSecretMap(context.Background(), *v.ref)
  147. if !ErrorContains(err, v.expectError) {
  148. t.Errorf("[%d] unexpected error: %s, expected: '%s'", k, err.Error(), v.expectError)
  149. }
  150. if err == nil && !reflect.DeepEqual(out, v.expectedData) {
  151. t.Errorf("[%d] unexpected secret data: expected %#v, got %#v", k, v.expectedData, out)
  152. }
  153. }
  154. }
  155. func TestValidateAccessKeyStore(t *testing.T) {
  156. kms := KeyManagementService{}
  157. store := &esv1.SecretStore{
  158. Spec: esv1.SecretStoreSpec{
  159. Provider: &esv1.SecretStoreProvider{
  160. Alibaba: &esv1.AlibabaProvider{
  161. RegionID: "region-1",
  162. Auth: esv1.AlibabaAuth{
  163. SecretRef: &esv1.AlibabaAuthSecretRef{
  164. AccessKeyID: esmeta.SecretKeySelector{
  165. Name: "accessKeyID",
  166. Key: "key-1",
  167. },
  168. AccessKeySecret: esmeta.SecretKeySelector{
  169. Name: "accessKeySecret",
  170. Key: "key-1",
  171. },
  172. },
  173. },
  174. },
  175. },
  176. },
  177. }
  178. _, err := kms.ValidateStore(store)
  179. if err != nil {
  180. t.Error(err.Error())
  181. }
  182. }
  183. func TestValidateRRSAStore(t *testing.T) {
  184. kms := KeyManagementService{}
  185. store := &esv1.SecretStore{
  186. Spec: esv1.SecretStoreSpec{
  187. Provider: &esv1.SecretStoreProvider{
  188. Alibaba: &esv1.AlibabaProvider{
  189. RegionID: "region-1",
  190. Auth: esv1.AlibabaAuth{
  191. RRSAAuth: &esv1.AlibabaRRSAAuth{
  192. OIDCProviderARN: "acs:ram::1234:oidc-provider/ack-rrsa-ce123456",
  193. OIDCTokenFilePath: "/var/run/secrets/tokens/oidc-token",
  194. RoleARN: "acs:ram::1234:role/test-role",
  195. SessionName: "secrets",
  196. },
  197. },
  198. },
  199. },
  200. },
  201. }
  202. _, err := kms.ValidateStore(store)
  203. if err != nil {
  204. t.Error(err.Error())
  205. }
  206. }
  207. func ErrorContains(out error, want string) bool {
  208. if out == nil {
  209. return want == ""
  210. }
  211. if want == "" {
  212. return false
  213. }
  214. return strings.Contains(out.Error(), want)
  215. }