provider_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. /*
  2. Licensed under the Apache License, Version 2.0 (the "License");
  3. you may not use this file except in compliance with the License.
  4. You may obtain a copy of the License at
  5. http://www.apache.org/licenses/LICENSE-2.0
  6. Unless required by applicable law or agreed to in writing, software
  7. distributed under the License is distributed on an "AS IS" BASIS,
  8. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. See the License for the specific language governing permissions and
  10. limitations under the License.
  11. */
  12. package delinea
  13. import (
  14. "context"
  15. "testing"
  16. "github.com/DelineaXPM/dsv-sdk-go/v2/vault"
  17. "github.com/stretchr/testify/assert"
  18. corev1 "k8s.io/api/core/v1"
  19. kubeErrors "k8s.io/apimachinery/pkg/api/errors"
  20. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  21. kubeClient "sigs.k8s.io/controller-runtime/pkg/client"
  22. clientfake "sigs.k8s.io/controller-runtime/pkg/client/fake"
  23. esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
  24. v1 "github.com/external-secrets/external-secrets/apis/meta/v1"
  25. "github.com/external-secrets/external-secrets/pkg/utils"
  26. )
  27. func TestDoesConfigDependOnNamespace(t *testing.T) {
  28. tests := map[string]struct {
  29. cfg esv1beta1.DelineaProvider
  30. want bool
  31. }{
  32. "true when client ID references a secret without explicit namespace": {
  33. cfg: esv1beta1.DelineaProvider{
  34. ClientID: &esv1beta1.DelineaProviderSecretRef{
  35. SecretRef: &v1.SecretKeySelector{Name: "foo"},
  36. },
  37. ClientSecret: &esv1beta1.DelineaProviderSecretRef{SecretRef: nil},
  38. },
  39. want: true,
  40. },
  41. "true when client secret references a secret without explicit namespace": {
  42. cfg: esv1beta1.DelineaProvider{
  43. ClientID: &esv1beta1.DelineaProviderSecretRef{SecretRef: nil},
  44. ClientSecret: &esv1beta1.DelineaProviderSecretRef{
  45. SecretRef: &v1.SecretKeySelector{Name: "foo"},
  46. },
  47. },
  48. want: true,
  49. },
  50. "false when neither client ID nor secret reference a secret": {
  51. cfg: esv1beta1.DelineaProvider{
  52. ClientID: &esv1beta1.DelineaProviderSecretRef{SecretRef: nil},
  53. ClientSecret: &esv1beta1.DelineaProviderSecretRef{SecretRef: nil},
  54. },
  55. want: false,
  56. },
  57. }
  58. for name, tc := range tests {
  59. t.Run(name, func(t *testing.T) {
  60. got := doesConfigDependOnNamespace(&tc.cfg)
  61. assert.Equal(t, tc.want, got)
  62. })
  63. }
  64. }
  65. func TestValidateStore(t *testing.T) {
  66. validSecretRefUsingValue := makeSecretRefUsingValue("foo")
  67. ambiguousSecretRef := &esv1beta1.DelineaProviderSecretRef{
  68. SecretRef: &v1.SecretKeySelector{Name: "foo"}, Value: "foo",
  69. }
  70. tests := map[string]struct {
  71. cfg esv1beta1.DelineaProvider
  72. want error
  73. }{
  74. "invalid without tenant": {
  75. cfg: esv1beta1.DelineaProvider{
  76. Tenant: "",
  77. ClientID: validSecretRefUsingValue,
  78. ClientSecret: validSecretRefUsingValue,
  79. },
  80. want: errEmptyTenant,
  81. },
  82. "invalid without clientID": {
  83. cfg: esv1beta1.DelineaProvider{
  84. Tenant: "foo",
  85. // ClientID omitted
  86. ClientSecret: validSecretRefUsingValue,
  87. },
  88. want: errEmptyClientID,
  89. },
  90. "invalid without clientSecret": {
  91. cfg: esv1beta1.DelineaProvider{
  92. Tenant: "foo",
  93. ClientID: validSecretRefUsingValue,
  94. // ClientSecret omitted
  95. },
  96. want: errEmptyClientSecret,
  97. },
  98. "invalid with ambiguous clientID": {
  99. cfg: esv1beta1.DelineaProvider{
  100. Tenant: "foo",
  101. ClientID: ambiguousSecretRef,
  102. ClientSecret: validSecretRefUsingValue,
  103. },
  104. want: errSecretRefAndValueConflict,
  105. },
  106. "invalid with ambiguous clientSecret": {
  107. cfg: esv1beta1.DelineaProvider{
  108. Tenant: "foo",
  109. ClientID: validSecretRefUsingValue,
  110. ClientSecret: ambiguousSecretRef,
  111. },
  112. want: errSecretRefAndValueConflict,
  113. },
  114. "invalid with invalid clientID": {
  115. cfg: esv1beta1.DelineaProvider{
  116. Tenant: "foo",
  117. ClientID: makeSecretRefUsingValue(""),
  118. ClientSecret: validSecretRefUsingValue,
  119. },
  120. want: errSecretRefAndValueMissing,
  121. },
  122. "invalid with invalid clientSecret": {
  123. cfg: esv1beta1.DelineaProvider{
  124. Tenant: "foo",
  125. ClientID: validSecretRefUsingValue,
  126. ClientSecret: makeSecretRefUsingValue(""),
  127. },
  128. want: errSecretRefAndValueMissing,
  129. },
  130. "valid with tenant/clientID/clientSecret": {
  131. cfg: esv1beta1.DelineaProvider{
  132. Tenant: "foo",
  133. ClientID: validSecretRefUsingValue,
  134. ClientSecret: validSecretRefUsingValue,
  135. },
  136. want: nil,
  137. },
  138. }
  139. for name, tc := range tests {
  140. t.Run(name, func(t *testing.T) {
  141. s := esv1beta1.SecretStore{
  142. Spec: esv1beta1.SecretStoreSpec{
  143. Provider: &esv1beta1.SecretStoreProvider{
  144. Delinea: &tc.cfg,
  145. },
  146. },
  147. }
  148. p := &Provider{}
  149. _, got := p.ValidateStore(&s)
  150. assert.Equal(t, tc.want, got)
  151. })
  152. }
  153. }
  154. func TestValidateStoreBailsOnUnexpectedStore(t *testing.T) {
  155. tests := map[string]struct {
  156. store esv1beta1.GenericStore
  157. want error
  158. }{
  159. "missing store": {nil, errMissingStore},
  160. "missing spec": {&esv1beta1.SecretStore{}, errInvalidSpec},
  161. "missing provider": {&esv1beta1.SecretStore{
  162. Spec: esv1beta1.SecretStoreSpec{Provider: nil},
  163. }, errInvalidSpec},
  164. "missing delinea": {&esv1beta1.SecretStore{
  165. Spec: esv1beta1.SecretStoreSpec{Provider: &esv1beta1.SecretStoreProvider{
  166. Delinea: nil,
  167. }},
  168. }, errInvalidSpec},
  169. }
  170. for name, tc := range tests {
  171. t.Run(name, func(t *testing.T) {
  172. p := &Provider{}
  173. _, got := p.ValidateStore(tc.store)
  174. assert.Equal(t, tc.want, got)
  175. })
  176. }
  177. }
  178. func TestNewClient(t *testing.T) {
  179. tenant := "foo"
  180. clientIDKey := "username"
  181. clientIDValue := "client id"
  182. clientSecretKey := "password"
  183. clientSecretValue := "client secret"
  184. clientSecret := &corev1.Secret{
  185. ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
  186. Data: map[string][]byte{
  187. clientIDKey: []byte(clientIDValue),
  188. clientSecretKey: []byte(clientSecretValue),
  189. },
  190. }
  191. validProvider := &esv1beta1.DelineaProvider{
  192. Tenant: tenant,
  193. ClientID: makeSecretRefUsingRef(clientSecret.Name, clientIDKey),
  194. ClientSecret: makeSecretRefUsingRef(clientSecret.Name, clientSecretKey),
  195. }
  196. tests := map[string]struct {
  197. store esv1beta1.GenericStore // leave nil for namespaced store
  198. provider *esv1beta1.DelineaProvider // discarded when store is set
  199. kube kubeClient.Client
  200. errCheck func(t *testing.T, err error)
  201. }{
  202. "missing provider config": {
  203. provider: nil,
  204. errCheck: func(t *testing.T, err error) {
  205. assert.ErrorIs(t, err, errInvalidSpec)
  206. },
  207. },
  208. "namespace-dependent cluster secret store": {
  209. store: &esv1beta1.ClusterSecretStore{
  210. TypeMeta: metav1.TypeMeta{Kind: esv1beta1.ClusterSecretStoreKind},
  211. Spec: esv1beta1.SecretStoreSpec{
  212. Provider: &esv1beta1.SecretStoreProvider{
  213. Delinea: validProvider,
  214. },
  215. },
  216. },
  217. errCheck: func(t *testing.T, err error) {
  218. assert.ErrorIs(t, err, errClusterStoreRequiresNamespace)
  219. },
  220. },
  221. "dangling client ID ref": {
  222. provider: &esv1beta1.DelineaProvider{
  223. Tenant: tenant,
  224. ClientID: makeSecretRefUsingRef("typo", clientIDKey),
  225. ClientSecret: makeSecretRefUsingRef(clientSecret.Name, clientSecretKey),
  226. },
  227. kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
  228. errCheck: func(t *testing.T, err error) {
  229. assert.True(t, kubeErrors.IsNotFound(err))
  230. },
  231. },
  232. "dangling client secret ref": {
  233. provider: &esv1beta1.DelineaProvider{
  234. Tenant: tenant,
  235. ClientID: makeSecretRefUsingRef(clientSecret.Name, clientIDKey),
  236. ClientSecret: makeSecretRefUsingRef("typo", clientSecretKey),
  237. },
  238. kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
  239. errCheck: func(t *testing.T, err error) {
  240. assert.True(t, kubeErrors.IsNotFound(err))
  241. },
  242. },
  243. "secret ref without name": {
  244. provider: &esv1beta1.DelineaProvider{
  245. Tenant: tenant,
  246. ClientID: makeSecretRefUsingRef("", clientIDKey),
  247. ClientSecret: makeSecretRefUsingRef(clientSecret.Name, clientSecretKey),
  248. },
  249. kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
  250. errCheck: func(t *testing.T, err error) {
  251. assert.ErrorIs(t, err, errMissingSecretName)
  252. },
  253. },
  254. "secret ref without key": {
  255. provider: &esv1beta1.DelineaProvider{
  256. Tenant: tenant,
  257. ClientID: makeSecretRefUsingRef(clientSecret.Name, ""),
  258. ClientSecret: makeSecretRefUsingRef(clientSecret.Name, clientSecretKey),
  259. },
  260. kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
  261. errCheck: func(t *testing.T, err error) {
  262. assert.ErrorIs(t, err, errMissingSecretKey)
  263. },
  264. },
  265. "secret ref with non-existent keys": {
  266. provider: &esv1beta1.DelineaProvider{
  267. Tenant: tenant,
  268. ClientID: makeSecretRefUsingRef(clientSecret.Name, "typo"),
  269. ClientSecret: makeSecretRefUsingRef(clientSecret.Name, clientSecretKey),
  270. },
  271. kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
  272. errCheck: func(t *testing.T, err error) {
  273. assert.EqualError(t, err, "cannot find secret data for key: \"typo\"")
  274. },
  275. },
  276. "valid secret refs": {
  277. provider: validProvider,
  278. kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
  279. },
  280. "secret values": {
  281. provider: &esv1beta1.DelineaProvider{
  282. Tenant: tenant,
  283. ClientID: makeSecretRefUsingValue(clientIDValue),
  284. ClientSecret: makeSecretRefUsingValue(clientSecretValue),
  285. },
  286. kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
  287. },
  288. "cluster secret store": {
  289. store: &esv1beta1.ClusterSecretStore{
  290. TypeMeta: metav1.TypeMeta{Kind: esv1beta1.ClusterSecretStoreKind},
  291. Spec: esv1beta1.SecretStoreSpec{
  292. Provider: &esv1beta1.SecretStoreProvider{
  293. Delinea: &esv1beta1.DelineaProvider{
  294. Tenant: tenant,
  295. ClientID: makeSecretRefUsingNamespacedRef(clientSecret.Namespace, clientSecret.Name, clientIDKey),
  296. ClientSecret: makeSecretRefUsingNamespacedRef(clientSecret.Namespace, clientSecret.Name, clientSecretKey),
  297. },
  298. },
  299. },
  300. },
  301. kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
  302. },
  303. }
  304. for name, tc := range tests {
  305. t.Run(name, func(t *testing.T) {
  306. p := &Provider{}
  307. store := tc.store
  308. if store == nil {
  309. store = &esv1beta1.SecretStore{
  310. TypeMeta: metav1.TypeMeta{Kind: esv1beta1.SecretStoreKind},
  311. Spec: esv1beta1.SecretStoreSpec{
  312. Provider: &esv1beta1.SecretStoreProvider{
  313. Delinea: tc.provider,
  314. },
  315. },
  316. }
  317. }
  318. sc, err := p.NewClient(context.Background(), store, tc.kube, clientSecret.Namespace)
  319. if tc.errCheck == nil {
  320. assert.NoError(t, err)
  321. delineaClient, ok := sc.(*client)
  322. assert.True(t, ok)
  323. dsvClient, ok := delineaClient.api.(*vault.Vault)
  324. assert.True(t, ok)
  325. assert.Equal(t, vault.Configuration{
  326. Credentials: vault.ClientCredential{
  327. ClientID: clientIDValue,
  328. ClientSecret: clientSecretValue,
  329. },
  330. Tenant: tenant,
  331. TLD: "com", // Default from Delinea
  332. URLTemplate: "https://%s.secretsvaultcloud.%s/v1/%s%s", // Default from Delinea
  333. }, dsvClient.Configuration)
  334. } else {
  335. assert.Nil(t, sc)
  336. tc.errCheck(t, err)
  337. }
  338. })
  339. }
  340. }
  341. func makeSecretRefUsingRef(name, key string) *esv1beta1.DelineaProviderSecretRef {
  342. return &esv1beta1.DelineaProviderSecretRef{
  343. SecretRef: &v1.SecretKeySelector{Name: name, Key: key},
  344. }
  345. }
  346. func makeSecretRefUsingNamespacedRef(namespace, name, key string) *esv1beta1.DelineaProviderSecretRef {
  347. return &esv1beta1.DelineaProviderSecretRef{
  348. SecretRef: &v1.SecretKeySelector{Namespace: utils.Ptr(namespace), Name: name, Key: key},
  349. }
  350. }
  351. func makeSecretRefUsingValue(val string) *esv1beta1.DelineaProviderSecretRef {
  352. return &esv1beta1.DelineaProviderSecretRef{Value: val}
  353. }