onepassword_test.go 56 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110
  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 onepassword
  13. import (
  14. "context"
  15. "errors"
  16. "fmt"
  17. "reflect"
  18. "testing"
  19. "github.com/1Password/connect-sdk-go/onepassword"
  20. corev1 "k8s.io/api/core/v1"
  21. apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
  22. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  23. pointer "k8s.io/utils/ptr"
  24. esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
  25. esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
  26. "github.com/external-secrets/external-secrets/pkg/provider/onepassword/fake"
  27. )
  28. const (
  29. // vaults and items.
  30. myVault, myVaultID = "my-vault", "my-vault-id"
  31. myItem, myItemID = "my-item", "my-item-id"
  32. mySharedVault, mySharedVaultID = "my-shared-vault", "my-shared-vault-id"
  33. mySharedItem, mySharedItemID = "my-shared-item", "my-shared-item-id"
  34. myOtherVault, myOtherVaultID = "my-other-vault", "my-other-vault-id"
  35. myOtherItem, myOtherItemID = "my-other-item", "my-other-item-id"
  36. myNonMatchingVault, myNonMatchingVaultID = "my-non-matching-vault", "my-non-matching-vault-id"
  37. myNonMatchingItem, myNonMatchingItemID = "my-non-matching-item", "my-non-matching-item-id"
  38. // fields and files.
  39. key1, key2, key3, key4 = "key1", "key2", "key3", "key4"
  40. value1, value2, value3, value4 = "value1", "value2", "value3", "value4"
  41. sharedKey1, sharedValue1 = "sharedkey1", "sharedvalue1"
  42. otherKey1 = "otherkey1"
  43. filePNG, filePNGID = "file.png", "file-id"
  44. myFilePNG, myFilePNGID, myContents = "my-file.png", "my-file-id", "my-contents"
  45. mySecondFileTXT, mySecondFileTXTID = "my-second-file.txt", "my-second-file-id"
  46. mySecondContents = "my-second-contents"
  47. myFile2PNG, myFile2TXT = "my-file-2.png", "my-file-2.txt"
  48. myFile2ID, myContents2 = "my-file-2-id", "my-contents-2"
  49. myOtherFilePNG, myOtherFilePNGID = "my-other-file.png", "my-other-file-id"
  50. myOtherContents = "my-other-contents"
  51. nonMatchingFilePNG, nonMatchingFilePNGID = "non-matching-file.png", "non-matching-file-id"
  52. nonMatchingContents = "non-matching-contents"
  53. // other.
  54. mySecret, token, password = "my-secret", "token", "password"
  55. one, two, three = "one", "two", "three"
  56. connectHost = "https://example.com"
  57. setupCheckFormat = "Setup: '%s', Check: '%s'"
  58. getSecretMapErrFormat = "%s: onepassword.GetSecretMap(...): -expected, +got:\n-%#v\n+%#v\n"
  59. getSecretErrFormat = "%s: onepassword.GetSecret(...): -expected, +got:\n-%#v\n+%#v\n"
  60. getAllSecretsErrFormat = "%s: onepassword.GetAllSecrets(...): -expected, +got:\n-%#v\n+%#v\n"
  61. validateStoreErrFormat = "%s: onepassword.validateStore(...): -expected, +got:\n-%#v\n+%#v\n"
  62. findItemErrFormat = "%s: onepassword.findItem(...): -expected, +got:\n-%#v\n+%#v\n"
  63. errFromErrMsgF = "%w: %s"
  64. errDoesNotMatchMsgF = "%s: error did not match: -expected, +got:\\n-%#v\\n+%#v\\n"
  65. )
  66. func TestFindItem(t *testing.T) {
  67. type check struct {
  68. checkNote string
  69. findItemName string
  70. expectedItem *onepassword.Item
  71. expectedErr error
  72. }
  73. type testCase struct {
  74. setupNote string
  75. provider *ProviderOnePassword
  76. checks []check
  77. }
  78. testCases := []testCase{
  79. {
  80. setupNote: "valid basic: one vault, one item, one field",
  81. provider: &ProviderOnePassword{
  82. vaults: map[string]int{myVault: 1},
  83. client: fake.NewMockClient().
  84. AddPredictableVault(myVault).
  85. AddPredictableItemWithField(myVault, myItem, key1, value1),
  86. },
  87. checks: []check{
  88. {
  89. checkNote: "pass",
  90. findItemName: myItem,
  91. expectedErr: nil,
  92. expectedItem: &onepassword.Item{
  93. ID: myItemID,
  94. Title: myItem,
  95. Vault: onepassword.ItemVault{ID: myVaultID},
  96. Fields: []*onepassword.ItemField{
  97. {
  98. Label: key1,
  99. Value: value1,
  100. },
  101. },
  102. },
  103. },
  104. },
  105. },
  106. {
  107. setupNote: "multiple vaults, multiple items",
  108. provider: &ProviderOnePassword{
  109. vaults: map[string]int{myVault: 1, mySharedVault: 2},
  110. client: fake.NewMockClient().
  111. AddPredictableVault(myVault).
  112. AddPredictableItemWithField(myVault, myItem, key1, value1).
  113. AddPredictableVault(mySharedVault).
  114. AddPredictableItemWithField(mySharedVault, mySharedItem, sharedKey1, sharedValue1),
  115. },
  116. checks: []check{
  117. {
  118. checkNote: "can still get myItem",
  119. findItemName: myItem,
  120. expectedErr: nil,
  121. expectedItem: &onepassword.Item{
  122. ID: myItemID,
  123. Title: myItem,
  124. Vault: onepassword.ItemVault{ID: myVaultID},
  125. Fields: []*onepassword.ItemField{
  126. {
  127. Label: key1,
  128. Value: value1,
  129. },
  130. },
  131. },
  132. },
  133. {
  134. checkNote: "can also get mySharedItem",
  135. findItemName: mySharedItem,
  136. expectedErr: nil,
  137. expectedItem: &onepassword.Item{
  138. ID: mySharedItemID,
  139. Title: mySharedItem,
  140. Vault: onepassword.ItemVault{ID: mySharedVaultID},
  141. Fields: []*onepassword.ItemField{
  142. {
  143. Label: sharedKey1,
  144. Value: sharedValue1,
  145. },
  146. },
  147. },
  148. },
  149. },
  150. },
  151. {
  152. setupNote: "multiple vault matches when should be one",
  153. provider: &ProviderOnePassword{
  154. vaults: map[string]int{myVault: 1, mySharedVault: 2},
  155. client: fake.NewMockClient().
  156. AppendVault(myVault, onepassword.Vault{
  157. ID: myVaultID,
  158. Name: myVault,
  159. }).
  160. AppendVault(myVault, onepassword.Vault{
  161. ID: "my-vault-extra-match-id",
  162. Name: "my-vault-extra-match",
  163. }),
  164. },
  165. checks: []check{
  166. {
  167. checkNote: "two vaults",
  168. findItemName: myItem,
  169. expectedErr: errors.New("key not found in 1Password Vaults: my-item in: map[my-shared-vault:2 my-vault:1]"),
  170. },
  171. },
  172. },
  173. {
  174. setupNote: "no item matches when should be one",
  175. provider: &ProviderOnePassword{
  176. vaults: map[string]int{myVault: 1},
  177. client: fake.NewMockClient().
  178. AddPredictableVault(myVault),
  179. },
  180. checks: []check{
  181. {
  182. checkNote: "no exist",
  183. findItemName: "my-item-no-exist",
  184. expectedErr: fmt.Errorf("%w: my-item-no-exist in: map[my-vault:1]", ErrKeyNotFound),
  185. },
  186. },
  187. },
  188. {
  189. setupNote: "multiple item matches when should be one",
  190. provider: &ProviderOnePassword{
  191. vaults: map[string]int{myVault: 1},
  192. client: fake.NewMockClient().
  193. AddPredictableVault(myVault).
  194. AddPredictableItemWithField(myVault, myItem, key1, value1).
  195. AppendItem(myVaultID, onepassword.Item{
  196. ID: "asdf",
  197. Title: myItem,
  198. Vault: onepassword.ItemVault{ID: myVaultID},
  199. }),
  200. },
  201. checks: []check{
  202. {
  203. checkNote: "multiple match",
  204. findItemName: myItem,
  205. expectedErr: fmt.Errorf(errFromErrMsgF, ErrExpectedOneItem, "'my-item', got 2"),
  206. },
  207. },
  208. },
  209. {
  210. setupNote: "ordered vaults",
  211. provider: &ProviderOnePassword{
  212. vaults: map[string]int{myVault: 1, mySharedVault: 2, myOtherVault: 3},
  213. client: fake.NewMockClient().
  214. AddPredictableVault(myVault).
  215. AddPredictableVault(mySharedVault).
  216. AddPredictableVault(myOtherVault).
  217. // // my-item
  218. // returned: my-item in my-vault
  219. AddPredictableItemWithField(myVault, myItem, key1, value1).
  220. // preempted: my-item in my-shared-vault
  221. AppendItem(mySharedVaultID, onepassword.Item{
  222. ID: myItemID,
  223. Title: myItem,
  224. Vault: onepassword.ItemVault{ID: mySharedVaultID},
  225. }).
  226. AppendItemField(mySharedVaultID, myItemID, onepassword.ItemField{
  227. Label: key1,
  228. Value: "value1-from-my-shared-vault",
  229. }).
  230. // preempted: my-item in my-other-vault
  231. AppendItem(myOtherVaultID, onepassword.Item{
  232. ID: myItemID,
  233. Title: myItem,
  234. Vault: onepassword.ItemVault{ID: myOtherVaultID},
  235. }).
  236. AppendItemField(myOtherVaultID, myItemID, onepassword.ItemField{
  237. Label: key1,
  238. Value: "value1-from-my-other-vault",
  239. }).
  240. // // my-shared-item
  241. // returned: my-shared-item in my-shared-vault
  242. AddPredictableItemWithField(mySharedVault, mySharedItem, sharedKey1, "sharedvalue1-from-my-shared-vault").
  243. // preempted: my-shared-item in my-other-vault
  244. AppendItem(myOtherVaultID, onepassword.Item{
  245. ID: mySharedItemID,
  246. Title: mySharedItem,
  247. Vault: onepassword.ItemVault{ID: myOtherVaultID},
  248. }).
  249. AppendItemField(myOtherVaultID, mySharedItemID, onepassword.ItemField{
  250. Label: sharedKey1,
  251. Value: "sharedvalue1-from-my-other-vault",
  252. }).
  253. // // my-other-item
  254. // returned: my-other-item in my-other-vault
  255. AddPredictableItemWithField(myOtherVault, myOtherItem, otherKey1, "othervalue1-from-my-other-vault"),
  256. },
  257. checks: []check{
  258. {
  259. // my-item in all three vaults, gets the one from my-vault
  260. checkNote: "gets item from my-vault",
  261. findItemName: myItem,
  262. expectedErr: nil,
  263. expectedItem: &onepassword.Item{
  264. ID: myItemID,
  265. Title: myItem,
  266. Vault: onepassword.ItemVault{ID: myVaultID},
  267. Fields: []*onepassword.ItemField{
  268. {
  269. Label: key1,
  270. Value: value1,
  271. },
  272. },
  273. },
  274. },
  275. {
  276. // my-shared-item in my-shared-vault and my-other-vault, gets the one from my-shared-vault
  277. checkNote: "gets item from my-shared-vault",
  278. findItemName: mySharedItem,
  279. expectedErr: nil,
  280. expectedItem: &onepassword.Item{
  281. ID: mySharedItemID,
  282. Title: mySharedItem,
  283. Vault: onepassword.ItemVault{ID: mySharedVaultID},
  284. Fields: []*onepassword.ItemField{
  285. {
  286. Label: sharedKey1,
  287. Value: "sharedvalue1-from-my-shared-vault",
  288. },
  289. },
  290. },
  291. },
  292. {
  293. // my-other-item in my-other-vault
  294. checkNote: "gets item from my-other-vault",
  295. findItemName: myOtherItem,
  296. expectedErr: nil,
  297. expectedItem: &onepassword.Item{
  298. ID: myOtherItemID,
  299. Title: myOtherItem,
  300. Vault: onepassword.ItemVault{ID: myOtherVaultID},
  301. Fields: []*onepassword.ItemField{
  302. {
  303. Label: otherKey1,
  304. Value: "othervalue1-from-my-other-vault",
  305. },
  306. },
  307. },
  308. },
  309. },
  310. },
  311. }
  312. // run the tests
  313. for _, tc := range testCases {
  314. for _, check := range tc.checks {
  315. got, err := tc.provider.findItem(check.findItemName)
  316. notes := fmt.Sprintf(setupCheckFormat, tc.setupNote, check.checkNote)
  317. if check.expectedErr == nil && err != nil {
  318. // expected no error, got one
  319. t.Errorf(findItemErrFormat, notes, nil, err)
  320. }
  321. if check.expectedErr != nil && err == nil {
  322. // expected an error, didn't get one
  323. t.Errorf(findItemErrFormat, notes, check.expectedErr.Error(), nil)
  324. }
  325. if check.expectedErr != nil && err != nil && err.Error() != check.expectedErr.Error() {
  326. // expected an error, got the wrong one
  327. t.Errorf(findItemErrFormat, notes, check.expectedErr.Error(), err.Error())
  328. }
  329. if check.expectedItem != nil {
  330. if !reflect.DeepEqual(check.expectedItem, got) {
  331. // expected a predefined item, got something else
  332. t.Errorf(findItemErrFormat, notes, check.expectedItem, got)
  333. }
  334. }
  335. }
  336. }
  337. }
  338. func TestValidateStore(t *testing.T) {
  339. type testCase struct {
  340. checkNote string
  341. store *esv1beta1.SecretStore
  342. clusterStore *esv1beta1.ClusterSecretStore
  343. expectedErr error
  344. }
  345. testCases := []testCase{
  346. {
  347. checkNote: "invalid: nil provider",
  348. store: &esv1beta1.SecretStore{
  349. TypeMeta: metav1.TypeMeta{
  350. Kind: "SecretStore",
  351. },
  352. Spec: esv1beta1.SecretStoreSpec{
  353. Provider: nil,
  354. },
  355. },
  356. expectedErr: fmt.Errorf(errOnePasswordStore, errors.New(errOnePasswordStoreNilSpecProvider)),
  357. },
  358. {
  359. checkNote: "invalid: nil OnePassword provider spec",
  360. store: &esv1beta1.SecretStore{
  361. TypeMeta: metav1.TypeMeta{
  362. Kind: "SecretStore",
  363. },
  364. Spec: esv1beta1.SecretStoreSpec{
  365. Provider: &esv1beta1.SecretStoreProvider{
  366. OnePassword: nil,
  367. },
  368. },
  369. },
  370. expectedErr: fmt.Errorf(errOnePasswordStore, errors.New(errOnePasswordStoreNilSpecProviderOnePassword)),
  371. },
  372. {
  373. checkNote: "valid secretStore",
  374. store: &esv1beta1.SecretStore{
  375. TypeMeta: metav1.TypeMeta{
  376. Kind: "SecretStore",
  377. },
  378. Spec: esv1beta1.SecretStoreSpec{
  379. Provider: &esv1beta1.SecretStoreProvider{
  380. OnePassword: &esv1beta1.OnePasswordProvider{
  381. Auth: &esv1beta1.OnePasswordAuth{
  382. SecretRef: &esv1beta1.OnePasswordAuthSecretRef{
  383. ConnectToken: esmeta.SecretKeySelector{
  384. Name: mySecret,
  385. Key: token,
  386. },
  387. },
  388. },
  389. ConnectHost: connectHost,
  390. Vaults: map[string]int{
  391. myVault: 1,
  392. },
  393. },
  394. },
  395. },
  396. },
  397. expectedErr: nil,
  398. },
  399. {
  400. checkNote: "invalid: illegal namespace on SecretStore",
  401. store: &esv1beta1.SecretStore{
  402. TypeMeta: metav1.TypeMeta{
  403. Kind: "SecretStore",
  404. },
  405. Spec: esv1beta1.SecretStoreSpec{
  406. Provider: &esv1beta1.SecretStoreProvider{
  407. OnePassword: &esv1beta1.OnePasswordProvider{
  408. Auth: &esv1beta1.OnePasswordAuth{
  409. SecretRef: &esv1beta1.OnePasswordAuthSecretRef{
  410. ConnectToken: esmeta.SecretKeySelector{
  411. Name: mySecret,
  412. Namespace: pointer.To("my-namespace"),
  413. Key: token,
  414. },
  415. },
  416. },
  417. ConnectHost: connectHost,
  418. Vaults: map[string]int{
  419. myVault: 1,
  420. myOtherVault: 2,
  421. },
  422. },
  423. },
  424. },
  425. },
  426. expectedErr: fmt.Errorf(errOnePasswordStore, errors.New("namespace should either be empty or match the namespace of the SecretStore for a namespaced SecretStore")),
  427. },
  428. {
  429. checkNote: "invalid: more than one vault with the same number",
  430. store: &esv1beta1.SecretStore{
  431. TypeMeta: metav1.TypeMeta{
  432. Kind: "SecretStore",
  433. },
  434. Spec: esv1beta1.SecretStoreSpec{
  435. Provider: &esv1beta1.SecretStoreProvider{
  436. OnePassword: &esv1beta1.OnePasswordProvider{
  437. Auth: &esv1beta1.OnePasswordAuth{
  438. SecretRef: &esv1beta1.OnePasswordAuthSecretRef{
  439. ConnectToken: esmeta.SecretKeySelector{
  440. Name: mySecret,
  441. Key: token,
  442. },
  443. },
  444. },
  445. ConnectHost: connectHost,
  446. Vaults: map[string]int{
  447. myVault: 1,
  448. myOtherVault: 1,
  449. },
  450. },
  451. },
  452. },
  453. },
  454. expectedErr: fmt.Errorf(errOnePasswordStore, errors.New(errOnePasswordStoreNonUniqueVaultNumbers)),
  455. },
  456. {
  457. checkNote: "valid: clusterSecretStore",
  458. clusterStore: &esv1beta1.ClusterSecretStore{
  459. TypeMeta: metav1.TypeMeta{
  460. Kind: "ClusterSecretStore",
  461. },
  462. Spec: esv1beta1.SecretStoreSpec{
  463. Provider: &esv1beta1.SecretStoreProvider{
  464. OnePassword: &esv1beta1.OnePasswordProvider{
  465. Auth: &esv1beta1.OnePasswordAuth{
  466. SecretRef: &esv1beta1.OnePasswordAuthSecretRef{
  467. ConnectToken: esmeta.SecretKeySelector{
  468. Name: mySecret,
  469. Namespace: pointer.To("my-namespace"),
  470. Key: token,
  471. },
  472. },
  473. },
  474. ConnectHost: connectHost,
  475. Vaults: map[string]int{
  476. myVault: 1,
  477. },
  478. },
  479. },
  480. },
  481. },
  482. expectedErr: nil,
  483. },
  484. {
  485. checkNote: "invalid: clusterSecretStore without namespace",
  486. clusterStore: &esv1beta1.ClusterSecretStore{
  487. TypeMeta: metav1.TypeMeta{
  488. Kind: "ClusterSecretStore",
  489. },
  490. Spec: esv1beta1.SecretStoreSpec{
  491. Provider: &esv1beta1.SecretStoreProvider{
  492. OnePassword: &esv1beta1.OnePasswordProvider{
  493. Auth: &esv1beta1.OnePasswordAuth{
  494. SecretRef: &esv1beta1.OnePasswordAuthSecretRef{
  495. ConnectToken: esmeta.SecretKeySelector{
  496. Name: mySecret,
  497. Key: token,
  498. },
  499. },
  500. },
  501. ConnectHost: connectHost,
  502. Vaults: map[string]int{
  503. myVault: 1,
  504. myOtherVault: 2,
  505. },
  506. },
  507. },
  508. },
  509. },
  510. expectedErr: fmt.Errorf(errOnePasswordStore, errors.New("cluster scope requires namespace")),
  511. },
  512. {
  513. checkNote: "invalid: missing connectTokenSecretRef.name",
  514. store: &esv1beta1.SecretStore{
  515. TypeMeta: metav1.TypeMeta{
  516. Kind: "SecretStore",
  517. },
  518. Spec: esv1beta1.SecretStoreSpec{
  519. Provider: &esv1beta1.SecretStoreProvider{
  520. OnePassword: &esv1beta1.OnePasswordProvider{
  521. Auth: &esv1beta1.OnePasswordAuth{
  522. SecretRef: &esv1beta1.OnePasswordAuthSecretRef{
  523. ConnectToken: esmeta.SecretKeySelector{
  524. Key: token,
  525. },
  526. },
  527. },
  528. ConnectHost: connectHost,
  529. Vaults: map[string]int{
  530. myVault: 1,
  531. myOtherVault: 2,
  532. },
  533. },
  534. },
  535. },
  536. },
  537. expectedErr: fmt.Errorf(errOnePasswordStore, errors.New(errOnePasswordStoreMissingRefName)),
  538. },
  539. {
  540. checkNote: "invalid: missing connectTokenSecretRef.key",
  541. store: &esv1beta1.SecretStore{
  542. TypeMeta: metav1.TypeMeta{
  543. Kind: "SecretStore",
  544. },
  545. Spec: esv1beta1.SecretStoreSpec{
  546. Provider: &esv1beta1.SecretStoreProvider{
  547. OnePassword: &esv1beta1.OnePasswordProvider{
  548. Auth: &esv1beta1.OnePasswordAuth{
  549. SecretRef: &esv1beta1.OnePasswordAuthSecretRef{
  550. ConnectToken: esmeta.SecretKeySelector{
  551. Name: mySecret,
  552. },
  553. },
  554. },
  555. ConnectHost: connectHost,
  556. Vaults: map[string]int{
  557. myVault: 1,
  558. myOtherVault: 2,
  559. },
  560. },
  561. },
  562. },
  563. },
  564. expectedErr: fmt.Errorf(errOnePasswordStore, errors.New(errOnePasswordStoreMissingRefKey)),
  565. },
  566. {
  567. checkNote: "invalid: at least one vault",
  568. store: &esv1beta1.SecretStore{
  569. TypeMeta: metav1.TypeMeta{
  570. Kind: "SecretStore",
  571. },
  572. Spec: esv1beta1.SecretStoreSpec{
  573. Provider: &esv1beta1.SecretStoreProvider{
  574. OnePassword: &esv1beta1.OnePasswordProvider{
  575. Auth: &esv1beta1.OnePasswordAuth{
  576. SecretRef: &esv1beta1.OnePasswordAuthSecretRef{
  577. ConnectToken: esmeta.SecretKeySelector{
  578. Name: mySecret,
  579. Key: token,
  580. },
  581. },
  582. },
  583. ConnectHost: connectHost,
  584. Vaults: map[string]int{},
  585. },
  586. },
  587. },
  588. },
  589. expectedErr: fmt.Errorf(errOnePasswordStore, errors.New(errOnePasswordStoreAtLeastOneVault)),
  590. },
  591. {
  592. checkNote: "invalid: url",
  593. store: &esv1beta1.SecretStore{
  594. TypeMeta: metav1.TypeMeta{
  595. Kind: "SecretStore",
  596. },
  597. Spec: esv1beta1.SecretStoreSpec{
  598. Provider: &esv1beta1.SecretStoreProvider{
  599. OnePassword: &esv1beta1.OnePasswordProvider{
  600. Auth: &esv1beta1.OnePasswordAuth{
  601. SecretRef: &esv1beta1.OnePasswordAuthSecretRef{
  602. ConnectToken: esmeta.SecretKeySelector{
  603. Name: mySecret,
  604. Key: token,
  605. },
  606. },
  607. },
  608. ConnectHost: ":/invalid.invalid",
  609. Vaults: map[string]int{
  610. myVault: 1,
  611. },
  612. },
  613. },
  614. },
  615. },
  616. expectedErr: fmt.Errorf(errOnePasswordStore, fmt.Errorf(errOnePasswordStoreInvalidConnectHost, errors.New("parse \":/invalid.invalid\": missing protocol scheme"))),
  617. },
  618. }
  619. // run the tests
  620. for _, tc := range testCases {
  621. var err error
  622. if tc.store == nil {
  623. err = validateStore(tc.clusterStore)
  624. } else {
  625. err = validateStore(tc.store)
  626. }
  627. notes := fmt.Sprintf("Check: '%s'", tc.checkNote)
  628. if tc.expectedErr == nil && err != nil {
  629. // expected no error, got one
  630. t.Errorf(validateStoreErrFormat, notes, nil, err)
  631. }
  632. if tc.expectedErr != nil && err == nil {
  633. // expected an error, didn't get one
  634. t.Errorf(validateStoreErrFormat, notes, tc.expectedErr.Error(), nil)
  635. }
  636. if tc.expectedErr != nil && err != nil && err.Error() != tc.expectedErr.Error() {
  637. // expected an error, got the wrong one
  638. t.Errorf(validateStoreErrFormat, notes, tc.expectedErr.Error(), err.Error())
  639. }
  640. }
  641. }
  642. // most functionality is tested in TestFindItem
  643. //
  644. // here we just check that an empty Property defaults to "password",
  645. // files are loaded, and
  646. // the data or errors are properly returned
  647. func TestGetSecret(t *testing.T) {
  648. type check struct {
  649. checkNote string
  650. ref esv1beta1.ExternalSecretDataRemoteRef
  651. expectedValue string
  652. expectedErr error
  653. }
  654. type testCase struct {
  655. setupNote string
  656. provider *ProviderOnePassword
  657. checks []check
  658. }
  659. testCases := []testCase{
  660. {
  661. setupNote: "one vault, one item, two fields",
  662. provider: &ProviderOnePassword{
  663. vaults: map[string]int{myVault: 1},
  664. client: fake.NewMockClient().
  665. AddPredictableVault(myVault).
  666. AddPredictableItemWithField(myVault, myItem, key1, value1).
  667. AppendItemField(myVaultID, myItemID, onepassword.ItemField{
  668. Label: password,
  669. Value: value2,
  670. }),
  671. },
  672. checks: []check{
  673. {
  674. checkNote: key1,
  675. ref: esv1beta1.ExternalSecretDataRemoteRef{
  676. Key: myItem,
  677. Property: key1,
  678. },
  679. expectedValue: value1,
  680. expectedErr: nil,
  681. },
  682. {
  683. checkNote: "'password' (defaulted property)",
  684. ref: esv1beta1.ExternalSecretDataRemoteRef{
  685. Key: myItem,
  686. },
  687. expectedValue: value2,
  688. expectedErr: nil,
  689. },
  690. {
  691. checkNote: "'ref.version' not implemented",
  692. ref: esv1beta1.ExternalSecretDataRemoteRef{
  693. Key: myItem,
  694. Property: key1,
  695. Version: "123",
  696. },
  697. expectedErr: errors.New(errVersionNotImplemented),
  698. },
  699. },
  700. },
  701. {
  702. setupNote: "files are loaded",
  703. provider: &ProviderOnePassword{
  704. vaults: map[string]int{myVault: 1},
  705. client: fake.NewMockClient().
  706. AddPredictableVault(myVault).
  707. AppendItem(myVaultID, onepassword.Item{
  708. ID: myItemID,
  709. Title: myItem,
  710. Vault: onepassword.ItemVault{ID: myVaultID},
  711. Category: documentCategory,
  712. Files: []*onepassword.File{
  713. {
  714. ID: myFilePNGID,
  715. Name: myFilePNG,
  716. },
  717. },
  718. }).
  719. SetFileContents(myFilePNG, []byte(myContents)),
  720. },
  721. checks: []check{
  722. {
  723. checkNote: "file named my-file.png",
  724. ref: esv1beta1.ExternalSecretDataRemoteRef{
  725. Key: myItem,
  726. Property: myFilePNG,
  727. },
  728. expectedValue: myContents,
  729. expectedErr: nil,
  730. },
  731. {
  732. checkNote: "empty ref.Property",
  733. ref: esv1beta1.ExternalSecretDataRemoteRef{
  734. Key: myItem,
  735. },
  736. expectedValue: myContents,
  737. expectedErr: nil,
  738. },
  739. {
  740. checkNote: "file non existent",
  741. ref: esv1beta1.ExternalSecretDataRemoteRef{
  742. Key: myItem,
  743. Property: "you-cant-find-me.png",
  744. },
  745. expectedErr: fmt.Errorf(errDocumentNotFound, errors.New("'my-item', 'you-cant-find-me.png'")),
  746. },
  747. },
  748. },
  749. {
  750. setupNote: "one vault, one item, two fields w/ same Label",
  751. provider: &ProviderOnePassword{
  752. vaults: map[string]int{myVault: 1},
  753. client: fake.NewMockClient().
  754. AddPredictableVault(myVault).
  755. AddPredictableItemWithField(myVault, myItem, key1, value1).
  756. AppendItemField(myVaultID, myItemID, onepassword.ItemField{
  757. Label: key1,
  758. Value: value2,
  759. }),
  760. },
  761. checks: []check{
  762. {
  763. checkNote: key1,
  764. ref: esv1beta1.ExternalSecretDataRemoteRef{
  765. Key: myItem,
  766. Property: key1,
  767. },
  768. expectedErr: fmt.Errorf(errFromErrMsgF, ErrExpectedOneField, "'key1' in 'my-item', got 2"),
  769. },
  770. },
  771. },
  772. }
  773. // run the tests
  774. for _, tc := range testCases {
  775. for _, check := range tc.checks {
  776. got, err := tc.provider.GetSecret(context.Background(), check.ref)
  777. notes := fmt.Sprintf(setupCheckFormat, tc.setupNote, check.checkNote)
  778. if check.expectedErr == nil && err != nil {
  779. // expected no error, got one
  780. t.Errorf(getSecretErrFormat, notes, nil, err)
  781. }
  782. if check.expectedErr != nil && err == nil {
  783. // expected an error, didn't get one
  784. t.Errorf(getSecretErrFormat, notes, check.expectedErr.Error(), nil)
  785. }
  786. if check.expectedErr != nil && err != nil && err.Error() != check.expectedErr.Error() {
  787. // expected an error, got the wrong one
  788. t.Errorf(getSecretErrFormat, notes, check.expectedErr.Error(), err.Error())
  789. }
  790. if check.expectedValue != "" {
  791. if check.expectedValue != string(got) {
  792. // expected a predefined value, got something else
  793. t.Errorf(getSecretErrFormat, notes, check.expectedValue, string(got))
  794. }
  795. }
  796. }
  797. }
  798. }
  799. // most functionality is tested in TestFindItem. here we just check:
  800. //
  801. // all keys are fetched and the map is compiled correctly,
  802. // files are loaded, and the data or errors are properly returned.
  803. func TestGetSecretMap(t *testing.T) {
  804. type check struct {
  805. checkNote string
  806. ref esv1beta1.ExternalSecretDataRemoteRef
  807. expectedMap map[string][]byte
  808. expectedErr error
  809. }
  810. type testCase struct {
  811. setupNote string
  812. provider *ProviderOnePassword
  813. checks []check
  814. }
  815. testCases := []testCase{
  816. {
  817. setupNote: "one vault, one item, two fields",
  818. provider: &ProviderOnePassword{
  819. vaults: map[string]int{myVault: 1},
  820. client: fake.NewMockClient().
  821. AddPredictableVault(myVault).
  822. AddPredictableItemWithField(myVault, myItem, key1, value1).
  823. AppendItemField(myVaultID, myItemID, onepassword.ItemField{
  824. Label: password,
  825. Value: value2,
  826. }),
  827. },
  828. checks: []check{
  829. {
  830. checkNote: "all Properties",
  831. ref: esv1beta1.ExternalSecretDataRemoteRef{
  832. Key: myItem,
  833. },
  834. expectedMap: map[string][]byte{
  835. key1: []byte(value1),
  836. password: []byte(value2),
  837. },
  838. expectedErr: nil,
  839. },
  840. {
  841. checkNote: "limit by Property",
  842. ref: esv1beta1.ExternalSecretDataRemoteRef{
  843. Key: myItem,
  844. Property: password,
  845. },
  846. expectedMap: map[string][]byte{
  847. password: []byte(value2),
  848. },
  849. expectedErr: nil,
  850. },
  851. {
  852. checkNote: "'ref.version' not implemented",
  853. ref: esv1beta1.ExternalSecretDataRemoteRef{
  854. Key: myItem,
  855. Property: key1,
  856. Version: "123",
  857. },
  858. expectedErr: errors.New(errVersionNotImplemented),
  859. },
  860. },
  861. },
  862. {
  863. setupNote: "files",
  864. provider: &ProviderOnePassword{
  865. vaults: map[string]int{myVault: 1},
  866. client: fake.NewMockClient().
  867. AddPredictableVault(myVault).
  868. AppendItem(myVaultID, onepassword.Item{
  869. ID: myItemID,
  870. Title: myItem,
  871. Vault: onepassword.ItemVault{ID: myVaultID},
  872. Category: documentCategory,
  873. Files: []*onepassword.File{
  874. {
  875. ID: myFilePNGID,
  876. Name: myFilePNG,
  877. },
  878. {
  879. ID: myFile2ID,
  880. Name: myFile2PNG,
  881. },
  882. },
  883. }).
  884. SetFileContents(myFilePNG, []byte(myContents)).
  885. SetFileContents(myFile2PNG, []byte(myContents2)),
  886. },
  887. checks: []check{
  888. {
  889. checkNote: "all Properties",
  890. ref: esv1beta1.ExternalSecretDataRemoteRef{
  891. Key: myItem,
  892. },
  893. expectedMap: map[string][]byte{
  894. myFilePNG: []byte(myContents),
  895. myFile2PNG: []byte(myContents2),
  896. },
  897. expectedErr: nil,
  898. },
  899. {
  900. checkNote: "limit by Property",
  901. ref: esv1beta1.ExternalSecretDataRemoteRef{
  902. Key: myItem,
  903. Property: myFilePNG,
  904. },
  905. expectedMap: map[string][]byte{
  906. myFilePNG: []byte(myContents),
  907. },
  908. expectedErr: nil,
  909. },
  910. },
  911. },
  912. {
  913. setupNote: "one vault, one item, two fields w/ same Label",
  914. provider: &ProviderOnePassword{
  915. vaults: map[string]int{myVault: 1},
  916. client: fake.NewMockClient().
  917. AddPredictableVault(myVault).
  918. AddPredictableItemWithField(myVault, myItem, key1, value1).
  919. AppendItemField(myVaultID, myItemID, onepassword.ItemField{
  920. Label: key1,
  921. Value: value2,
  922. }),
  923. },
  924. checks: []check{
  925. {
  926. checkNote: key1,
  927. ref: esv1beta1.ExternalSecretDataRemoteRef{
  928. Key: myItem,
  929. },
  930. expectedMap: nil,
  931. expectedErr: fmt.Errorf(errFromErrMsgF, ErrExpectedOneField, "'key1' in 'my-item', got 2"),
  932. },
  933. },
  934. },
  935. }
  936. // run the tests
  937. for _, tc := range testCases {
  938. for _, check := range tc.checks {
  939. gotMap, err := tc.provider.GetSecretMap(context.Background(), check.ref)
  940. notes := fmt.Sprintf(setupCheckFormat, tc.setupNote, check.checkNote)
  941. if check.expectedErr == nil && err != nil {
  942. // expected no error, got one
  943. t.Errorf(getSecretMapErrFormat, notes, nil, err)
  944. }
  945. if check.expectedErr != nil && err == nil {
  946. // expected an error, didn't get one
  947. t.Errorf(getSecretMapErrFormat, notes, check.expectedErr.Error(), nil)
  948. }
  949. if check.expectedErr != nil && err != nil && err.Error() != check.expectedErr.Error() {
  950. // expected an error, got the wrong one
  951. t.Errorf(getSecretMapErrFormat, notes, check.expectedErr.Error(), err.Error())
  952. }
  953. if !reflect.DeepEqual(check.expectedMap, gotMap) {
  954. // expected a predefined map, got something else
  955. t.Errorf(getSecretMapErrFormat, notes, check.expectedMap, gotMap)
  956. }
  957. }
  958. }
  959. }
  960. func TestGetAllSecrets(t *testing.T) {
  961. type check struct {
  962. checkNote string
  963. ref esv1beta1.ExternalSecretFind
  964. expectedMap map[string][]byte
  965. expectedErr error
  966. }
  967. type testCase struct {
  968. setupNote string
  969. provider *ProviderOnePassword
  970. checks []check
  971. }
  972. testCases := []testCase{
  973. {
  974. setupNote: "three vaults, three items, all different field Labels",
  975. provider: &ProviderOnePassword{
  976. vaults: map[string]int{myVault: 1, myOtherVault: 2, myNonMatchingVault: 3},
  977. client: fake.NewMockClient().
  978. AddPredictableVault(myVault).
  979. AddPredictableItemWithField(myVault, myItem, key1, value1).
  980. AppendItemField(myVaultID, myItemID, onepassword.ItemField{
  981. Label: key2,
  982. Value: value2,
  983. }).
  984. AddPredictableVault(myOtherVault).
  985. AddPredictableItemWithField(myOtherVault, myOtherItem, key3, value3).
  986. AppendItemField(myOtherVaultID, myOtherItemID, onepassword.ItemField{
  987. Label: key4,
  988. Value: value4,
  989. }).
  990. AddPredictableVault(myNonMatchingVault).
  991. AddPredictableItemWithField(myNonMatchingVault, myNonMatchingItem, "non-matching5", "value5").
  992. AppendItemField(myNonMatchingVaultID, myNonMatchingItemID, onepassword.ItemField{
  993. Label: "non-matching6",
  994. Value: "value6",
  995. }),
  996. },
  997. checks: []check{
  998. {
  999. checkNote: "find some with path only",
  1000. ref: esv1beta1.ExternalSecretFind{
  1001. Path: pointer.To(myItem),
  1002. },
  1003. expectedMap: map[string][]byte{
  1004. key1: []byte(value1),
  1005. key2: []byte(value2),
  1006. },
  1007. expectedErr: nil,
  1008. },
  1009. {
  1010. checkNote: "find most with regex 'key*'",
  1011. ref: esv1beta1.ExternalSecretFind{
  1012. Name: &esv1beta1.FindName{
  1013. RegExp: "key*",
  1014. },
  1015. },
  1016. expectedMap: map[string][]byte{
  1017. key1: []byte(value1),
  1018. key2: []byte(value2),
  1019. key3: []byte(value3),
  1020. key4: []byte(value4),
  1021. },
  1022. expectedErr: nil,
  1023. },
  1024. {
  1025. checkNote: "find some with regex 'key*' and path 'my-other-item'",
  1026. ref: esv1beta1.ExternalSecretFind{
  1027. Name: &esv1beta1.FindName{
  1028. RegExp: "key*",
  1029. },
  1030. Path: pointer.To(myOtherItem),
  1031. },
  1032. expectedMap: map[string][]byte{
  1033. key3: []byte(value3),
  1034. key4: []byte(value4),
  1035. },
  1036. expectedErr: nil,
  1037. },
  1038. {
  1039. checkNote: "find none with regex 'asdf*'",
  1040. ref: esv1beta1.ExternalSecretFind{
  1041. Name: &esv1beta1.FindName{
  1042. RegExp: "asdf*",
  1043. },
  1044. },
  1045. expectedMap: map[string][]byte{},
  1046. expectedErr: nil,
  1047. },
  1048. {
  1049. checkNote: "find none with path 'no-exist'",
  1050. ref: esv1beta1.ExternalSecretFind{
  1051. Name: &esv1beta1.FindName{
  1052. RegExp: "key*",
  1053. },
  1054. Path: pointer.To("no-exist"),
  1055. },
  1056. expectedMap: map[string][]byte{},
  1057. expectedErr: nil,
  1058. },
  1059. {
  1060. checkNote: "error when find.tags",
  1061. ref: esv1beta1.ExternalSecretFind{
  1062. Name: &esv1beta1.FindName{
  1063. RegExp: "key*",
  1064. },
  1065. Tags: map[string]string{
  1066. "asdf": "fdas",
  1067. },
  1068. },
  1069. expectedErr: errors.New(errTagsNotImplemented),
  1070. },
  1071. },
  1072. },
  1073. {
  1074. setupNote: "3 vaults, 4 items, 5 files",
  1075. provider: &ProviderOnePassword{
  1076. vaults: map[string]int{myVault: 1, myOtherVault: 2, myNonMatchingVault: 3},
  1077. client: fake.NewMockClient().
  1078. // my-vault
  1079. AddPredictableVault(myVault).
  1080. AppendItem(myVaultID, onepassword.Item{
  1081. ID: myItemID,
  1082. Title: myItem,
  1083. Vault: onepassword.ItemVault{ID: myVaultID},
  1084. Category: documentCategory,
  1085. Files: []*onepassword.File{
  1086. {
  1087. ID: myFilePNGID,
  1088. Name: myFilePNG,
  1089. },
  1090. {
  1091. ID: mySecondFileTXTID,
  1092. Name: mySecondFileTXT,
  1093. },
  1094. },
  1095. }).
  1096. SetFileContents(myFilePNG, []byte(myContents)).
  1097. SetFileContents(mySecondFileTXT, []byte(mySecondContents)).
  1098. AppendItem(myVaultID, onepassword.Item{
  1099. ID: "my-item-2-id",
  1100. Title: "my-item-2",
  1101. Vault: onepassword.ItemVault{ID: myVaultID},
  1102. Category: documentCategory,
  1103. Files: []*onepassword.File{
  1104. {
  1105. ID: myFile2ID,
  1106. Name: myFile2TXT,
  1107. },
  1108. },
  1109. }).
  1110. SetFileContents(myFile2TXT, []byte(myContents2)).
  1111. // my-other-vault
  1112. AddPredictableVault(myOtherVault).
  1113. AppendItem(myOtherVaultID, onepassword.Item{
  1114. ID: myOtherItemID,
  1115. Title: myOtherItem,
  1116. Vault: onepassword.ItemVault{ID: myOtherVaultID},
  1117. Category: documentCategory,
  1118. Files: []*onepassword.File{
  1119. {
  1120. ID: myOtherFilePNGID,
  1121. Name: myOtherFilePNG,
  1122. },
  1123. },
  1124. }).
  1125. SetFileContents(myOtherFilePNG, []byte(myOtherContents)).
  1126. // my-non-matching-vault
  1127. AddPredictableVault(myNonMatchingVault).
  1128. AppendItem(myNonMatchingVaultID, onepassword.Item{
  1129. ID: myNonMatchingItemID,
  1130. Title: myNonMatchingItem,
  1131. Vault: onepassword.ItemVault{ID: myNonMatchingVaultID},
  1132. Category: documentCategory,
  1133. Files: []*onepassword.File{
  1134. {
  1135. ID: nonMatchingFilePNGID,
  1136. Name: nonMatchingFilePNG,
  1137. },
  1138. },
  1139. }).
  1140. SetFileContents(nonMatchingFilePNG, []byte(nonMatchingContents)),
  1141. },
  1142. checks: []check{
  1143. {
  1144. checkNote: "find most with regex '^my-*'",
  1145. ref: esv1beta1.ExternalSecretFind{
  1146. Name: &esv1beta1.FindName{
  1147. RegExp: "^my-*",
  1148. },
  1149. },
  1150. expectedMap: map[string][]byte{
  1151. myFilePNG: []byte(myContents),
  1152. mySecondFileTXT: []byte(mySecondContents),
  1153. myFile2TXT: []byte(myContents2),
  1154. myOtherFilePNG: []byte(myOtherContents),
  1155. },
  1156. expectedErr: nil,
  1157. },
  1158. {
  1159. checkNote: "find some with regex '^my-*' and path 'my-other-item'",
  1160. ref: esv1beta1.ExternalSecretFind{
  1161. Name: &esv1beta1.FindName{
  1162. RegExp: "^my-*",
  1163. },
  1164. Path: pointer.To(myOtherItem),
  1165. },
  1166. expectedMap: map[string][]byte{
  1167. myOtherFilePNG: []byte(myOtherContents),
  1168. },
  1169. expectedErr: nil,
  1170. },
  1171. {
  1172. checkNote: "find none with regex '^asdf*'",
  1173. ref: esv1beta1.ExternalSecretFind{
  1174. Name: &esv1beta1.FindName{
  1175. RegExp: "^asdf*",
  1176. },
  1177. },
  1178. expectedMap: map[string][]byte{},
  1179. expectedErr: nil,
  1180. },
  1181. {
  1182. checkNote: "find none with path 'no-exist'",
  1183. ref: esv1beta1.ExternalSecretFind{
  1184. Name: &esv1beta1.FindName{
  1185. RegExp: "^my-*",
  1186. },
  1187. Path: pointer.To("no-exist"),
  1188. },
  1189. expectedMap: map[string][]byte{},
  1190. expectedErr: nil,
  1191. },
  1192. },
  1193. },
  1194. {
  1195. setupNote: "two fields/files with same name, first one wins",
  1196. provider: &ProviderOnePassword{
  1197. vaults: map[string]int{myVault: 1, myOtherVault: 2},
  1198. client: fake.NewMockClient().
  1199. // my-vault
  1200. AddPredictableVault(myVault).
  1201. AddPredictableItemWithField(myVault, myItem, key1, value1).
  1202. AddPredictableItemWithField(myVault, "my-second-item", key1, "value-second").
  1203. AppendItem(myVaultID, onepassword.Item{
  1204. ID: "file-item-id",
  1205. Title: "file-item",
  1206. Vault: onepassword.ItemVault{ID: myVaultID},
  1207. Category: documentCategory,
  1208. Files: []*onepassword.File{
  1209. {
  1210. ID: filePNGID,
  1211. Name: filePNG,
  1212. },
  1213. },
  1214. }).
  1215. SetFileContents(filePNG, []byte(myContents)).
  1216. AppendItem(myVaultID, onepassword.Item{
  1217. ID: "file-item-2-id",
  1218. Title: "file-item-2",
  1219. Vault: onepassword.ItemVault{ID: myVaultID},
  1220. Category: documentCategory,
  1221. Files: []*onepassword.File{
  1222. {
  1223. ID: "file-2-id",
  1224. Name: filePNG,
  1225. },
  1226. },
  1227. }).
  1228. // my-other-vault
  1229. AddPredictableVault(myOtherVault).
  1230. AddPredictableItemWithField(myOtherVault, myOtherItem, key1, "value-other").
  1231. AppendItem(myOtherVaultID, onepassword.Item{
  1232. ID: "file-item-other-id",
  1233. Title: "file-item-other",
  1234. Vault: onepassword.ItemVault{ID: myOtherVaultID},
  1235. Category: documentCategory,
  1236. Files: []*onepassword.File{
  1237. {
  1238. ID: "other-file-id",
  1239. Name: filePNG,
  1240. },
  1241. },
  1242. }),
  1243. },
  1244. checks: []check{
  1245. {
  1246. checkNote: "find fields with regex '^key*'",
  1247. ref: esv1beta1.ExternalSecretFind{
  1248. Name: &esv1beta1.FindName{
  1249. RegExp: "^key*",
  1250. },
  1251. },
  1252. expectedMap: map[string][]byte{
  1253. key1: []byte(value1),
  1254. },
  1255. expectedErr: nil,
  1256. },
  1257. {
  1258. checkNote: "find files with regex '^file*item*'",
  1259. ref: esv1beta1.ExternalSecretFind{
  1260. Name: &esv1beta1.FindName{
  1261. RegExp: "^file*",
  1262. },
  1263. },
  1264. expectedMap: map[string][]byte{
  1265. filePNG: []byte(myContents),
  1266. },
  1267. expectedErr: nil,
  1268. },
  1269. },
  1270. },
  1271. }
  1272. // run the tests
  1273. for _, tc := range testCases {
  1274. for _, check := range tc.checks {
  1275. gotMap, err := tc.provider.GetAllSecrets(context.Background(), check.ref)
  1276. notes := fmt.Sprintf(setupCheckFormat, tc.setupNote, check.checkNote)
  1277. if check.expectedErr == nil && err != nil {
  1278. // expected no error, got one
  1279. t.Fatalf(getAllSecretsErrFormat, notes, nil, err)
  1280. }
  1281. if check.expectedErr != nil && err == nil {
  1282. // expected an error, didn't get one
  1283. t.Errorf(getAllSecretsErrFormat, notes, check.expectedErr.Error(), nil)
  1284. }
  1285. if check.expectedErr != nil && err != nil && err.Error() != check.expectedErr.Error() {
  1286. // expected an error, got the wrong one
  1287. t.Errorf(getAllSecretsErrFormat, notes, check.expectedErr.Error(), err.Error())
  1288. }
  1289. if !reflect.DeepEqual(check.expectedMap, gotMap) {
  1290. // expected a predefined map, got something else
  1291. t.Errorf(getAllSecretsErrFormat, notes, check.expectedMap, gotMap)
  1292. }
  1293. }
  1294. }
  1295. }
  1296. func TestSortVaults(t *testing.T) {
  1297. type testCase struct {
  1298. vaults map[string]int
  1299. expected []string
  1300. }
  1301. testCases := []testCase{
  1302. {
  1303. vaults: map[string]int{
  1304. one: 1,
  1305. three: 3,
  1306. two: 2,
  1307. },
  1308. expected: []string{
  1309. one,
  1310. two,
  1311. three,
  1312. },
  1313. },
  1314. {
  1315. vaults: map[string]int{
  1316. "four": 100,
  1317. one: 1,
  1318. three: 3,
  1319. two: 2,
  1320. },
  1321. expected: []string{
  1322. one,
  1323. two,
  1324. three,
  1325. "four",
  1326. },
  1327. },
  1328. }
  1329. // run the tests
  1330. for _, tc := range testCases {
  1331. got := sortVaults(tc.vaults)
  1332. if !reflect.DeepEqual(got, tc.expected) {
  1333. t.Errorf("onepassword.sortVaults(...): -expected, +got:\n-%#v\n+%#v\n", tc.expected, got)
  1334. }
  1335. }
  1336. }
  1337. func TestHasUniqueVaultNumbers(t *testing.T) {
  1338. type testCase struct {
  1339. vaults map[string]int
  1340. expected bool
  1341. }
  1342. testCases := []testCase{
  1343. {
  1344. vaults: map[string]int{
  1345. one: 1,
  1346. three: 3,
  1347. two: 2,
  1348. },
  1349. expected: true,
  1350. },
  1351. {
  1352. vaults: map[string]int{
  1353. "four": 100,
  1354. one: 1,
  1355. three: 3,
  1356. two: 2,
  1357. "eight": 100,
  1358. },
  1359. expected: false,
  1360. },
  1361. {
  1362. vaults: map[string]int{
  1363. one: 1,
  1364. "1": 1,
  1365. three: 3,
  1366. two: 2,
  1367. },
  1368. expected: false,
  1369. },
  1370. }
  1371. // run the tests
  1372. for _, tc := range testCases {
  1373. got := hasUniqueVaultNumbers(tc.vaults)
  1374. if got != tc.expected {
  1375. t.Errorf("onepassword.hasUniqueVaultNumbers(...): -expected, +got:\n-%#v\n+%#v\n", tc.expected, got)
  1376. }
  1377. }
  1378. }
  1379. type fakeRef struct {
  1380. key string
  1381. prop string
  1382. secretKey string
  1383. }
  1384. func (f fakeRef) GetRemoteKey() string {
  1385. return f.key
  1386. }
  1387. func (f fakeRef) GetProperty() string {
  1388. return f.prop
  1389. }
  1390. func (f fakeRef) GetSecretKey() string {
  1391. return f.secretKey
  1392. }
  1393. func (f fakeRef) GetMetadata() *apiextensionsv1.JSON {
  1394. return nil
  1395. }
  1396. func validateItem(t *testing.T, expectedItem, actualItem *onepassword.Item) {
  1397. t.Helper()
  1398. if !reflect.DeepEqual(expectedItem, actualItem) {
  1399. t.Errorf("expected item %v, got %v", expectedItem, actualItem)
  1400. }
  1401. }
  1402. func TestProviderOnePasswordCreateItem(t *testing.T) {
  1403. type testCase struct {
  1404. vaults map[string]int
  1405. expectedErr error
  1406. setupNote string
  1407. val []byte
  1408. createValidateFunc func(*testing.T, *onepassword.Item, string) (*onepassword.Item, error)
  1409. ref esv1beta1.PushSecretData
  1410. }
  1411. const vaultName = "vault1"
  1412. thridPartyErr := errors.New("third party error")
  1413. testCases := []testCase{
  1414. {
  1415. setupNote: "standard create",
  1416. val: []byte("value"),
  1417. ref: fakeRef{
  1418. key: "testing",
  1419. prop: "prop",
  1420. },
  1421. expectedErr: nil,
  1422. vaults: map[string]int{
  1423. vaultName: 1,
  1424. },
  1425. createValidateFunc: func(t *testing.T, item *onepassword.Item, s string) (*onepassword.Item, error) {
  1426. validateItem(t, &onepassword.Item{
  1427. Title: "testing",
  1428. Category: onepassword.Server,
  1429. Vault: onepassword.ItemVault{
  1430. ID: vaultName,
  1431. },
  1432. Fields: []*onepassword.ItemField{
  1433. generateNewItemField("prop", "value"),
  1434. },
  1435. }, item)
  1436. return item, nil
  1437. },
  1438. },
  1439. {
  1440. setupNote: "standard create with no property",
  1441. val: []byte("value2"),
  1442. ref: fakeRef{
  1443. key: "testing2",
  1444. prop: "",
  1445. },
  1446. vaults: map[string]int{
  1447. vaultName: 2,
  1448. },
  1449. createValidateFunc: func(t *testing.T, item *onepassword.Item, s string) (*onepassword.Item, error) {
  1450. validateItem(t, &onepassword.Item{
  1451. Title: "testing2",
  1452. Category: onepassword.Server,
  1453. Vault: onepassword.ItemVault{
  1454. ID: vaultName,
  1455. },
  1456. Fields: []*onepassword.ItemField{
  1457. generateNewItemField("password", "value2"),
  1458. },
  1459. }, item)
  1460. return item, nil
  1461. },
  1462. },
  1463. {
  1464. setupNote: "no vaults",
  1465. val: []byte("value"),
  1466. ref: fakeRef{
  1467. key: "testing",
  1468. prop: "prop",
  1469. },
  1470. vaults: map[string]int{},
  1471. expectedErr: ErrNoVaults,
  1472. createValidateFunc: func(t *testing.T, item *onepassword.Item, s string) (*onepassword.Item, error) {
  1473. t.Errorf("onepassword.createItem(...): should not have been called")
  1474. return nil, nil
  1475. },
  1476. },
  1477. {
  1478. setupNote: "error on create",
  1479. val: []byte("testing"),
  1480. ref: fakeRef{
  1481. key: "another",
  1482. prop: "property",
  1483. },
  1484. vaults: map[string]int{
  1485. vaultName: 1,
  1486. },
  1487. expectedErr: thridPartyErr,
  1488. createValidateFunc: func(t *testing.T, item *onepassword.Item, s string) (*onepassword.Item, error) {
  1489. validateItem(t, &onepassword.Item{
  1490. Title: "another",
  1491. Category: onepassword.Server,
  1492. Vault: onepassword.ItemVault{
  1493. ID: vaultName,
  1494. },
  1495. Fields: []*onepassword.ItemField{
  1496. generateNewItemField("property", "testing"),
  1497. },
  1498. }, item)
  1499. return nil, thridPartyErr
  1500. },
  1501. },
  1502. }
  1503. provider := &ProviderOnePassword{}
  1504. for _, tc := range testCases {
  1505. // setup
  1506. mockClient := fake.NewMockClient()
  1507. mockClient.CreateItemValidateFunc = func(item *onepassword.Item, s string) (*onepassword.Item, error) {
  1508. i, e := tc.createValidateFunc(t, item, s)
  1509. return i, e
  1510. }
  1511. provider.client = mockClient
  1512. provider.vaults = tc.vaults
  1513. err := provider.createItem(tc.val, tc.ref)
  1514. if !errors.Is(err, tc.expectedErr) {
  1515. t.Errorf(errDoesNotMatchMsgF, tc.setupNote, tc.expectedErr, err)
  1516. }
  1517. }
  1518. }
  1519. func TestProviderOnePasswordDeleteItem(t *testing.T) {
  1520. type testCase struct {
  1521. inputFields []*onepassword.ItemField
  1522. fieldName string
  1523. expectedErr error
  1524. expectedFields []*onepassword.ItemField
  1525. setupNote string
  1526. }
  1527. field1, field2, field3, field4 := "field1", "field2", "field3", "field4"
  1528. testCases := []testCase{
  1529. {
  1530. setupNote: "one field to remove",
  1531. inputFields: []*onepassword.ItemField{
  1532. {
  1533. ID: field1,
  1534. Label: field1,
  1535. Type: onepassword.FieldTypeAddress,
  1536. },
  1537. {
  1538. ID: field2,
  1539. Label: field2,
  1540. Type: onepassword.FieldTypeString,
  1541. },
  1542. {
  1543. ID: field3,
  1544. Label: field3,
  1545. Type: onepassword.FieldTypeConcealed,
  1546. },
  1547. },
  1548. fieldName: field2,
  1549. expectedFields: []*onepassword.ItemField{
  1550. {
  1551. ID: field1,
  1552. Label: field1,
  1553. Type: onepassword.FieldTypeAddress,
  1554. },
  1555. {
  1556. ID: field3,
  1557. Label: field3,
  1558. Type: onepassword.FieldTypeConcealed,
  1559. },
  1560. },
  1561. },
  1562. {
  1563. setupNote: "no fields to remove",
  1564. inputFields: []*onepassword.ItemField{
  1565. {
  1566. ID: field1,
  1567. Label: field1,
  1568. Type: onepassword.FieldTypeAddress,
  1569. },
  1570. {
  1571. ID: field2,
  1572. Label: field2,
  1573. Type: onepassword.FieldTypeString,
  1574. },
  1575. {
  1576. ID: field3,
  1577. Label: field3,
  1578. Type: onepassword.FieldTypeConcealed,
  1579. },
  1580. },
  1581. expectedErr: nil,
  1582. fieldName: field4,
  1583. expectedFields: []*onepassword.ItemField{
  1584. {
  1585. ID: field1,
  1586. Label: field1,
  1587. Type: onepassword.FieldTypeAddress,
  1588. },
  1589. {
  1590. ID: field2,
  1591. Label: field2,
  1592. Type: onepassword.FieldTypeString,
  1593. },
  1594. {
  1595. ID: field3,
  1596. Label: field3,
  1597. Type: onepassword.FieldTypeConcealed,
  1598. },
  1599. },
  1600. },
  1601. {
  1602. setupNote: "multiple fields to remove",
  1603. inputFields: []*onepassword.ItemField{
  1604. {
  1605. ID: field3,
  1606. Label: field3,
  1607. Type: onepassword.FieldTypeConcealed,
  1608. },
  1609. {
  1610. ID: field1,
  1611. Label: field1,
  1612. Type: onepassword.FieldTypeAddress,
  1613. },
  1614. {
  1615. ID: field3,
  1616. Label: field3,
  1617. Type: onepassword.FieldTypeCreditCardType,
  1618. },
  1619. {
  1620. ID: field2,
  1621. Label: field2,
  1622. Type: onepassword.FieldTypeString,
  1623. },
  1624. {
  1625. ID: field3,
  1626. Label: field3,
  1627. Type: onepassword.FieldTypeGender,
  1628. },
  1629. },
  1630. fieldName: field3,
  1631. expectedErr: ErrExpectedOneField,
  1632. expectedFields: nil,
  1633. },
  1634. }
  1635. // run the tests
  1636. for _, tc := range testCases {
  1637. actualOutput, err := deleteField(tc.inputFields, tc.fieldName)
  1638. if len(actualOutput) != len(tc.expectedFields) {
  1639. t.Errorf("%s: length fields did not match: -expected, +got:\n-%#v\n+%#v\n", tc.setupNote, tc.expectedFields, actualOutput)
  1640. return
  1641. }
  1642. if !errors.Is(err, tc.expectedErr) {
  1643. t.Errorf(errDoesNotMatchMsgF, tc.setupNote, tc.expectedErr, err)
  1644. }
  1645. for i, check := range tc.expectedFields {
  1646. if len(actualOutput) <= i {
  1647. continue
  1648. }
  1649. if !reflect.DeepEqual(check, actualOutput[i]) {
  1650. t.Errorf("%s: fields at position %d did not match: -expected, +got:\n-%#v\n+%#v\n", tc.setupNote, i, check, actualOutput[i])
  1651. }
  1652. }
  1653. }
  1654. }
  1655. func TestUpdateFields(t *testing.T) {
  1656. type testCase struct {
  1657. inputFields []*onepassword.ItemField
  1658. fieldName string
  1659. newVal string
  1660. expectedErr error
  1661. expectedFields []*onepassword.ItemField
  1662. setupNote string
  1663. }
  1664. field1, field2, field3, field4 := "field1", "field2", "field3", "field4"
  1665. testCases := []testCase{
  1666. {
  1667. setupNote: "one field to update",
  1668. inputFields: []*onepassword.ItemField{
  1669. {
  1670. ID: field1,
  1671. Label: field1,
  1672. Value: value1,
  1673. Type: onepassword.FieldTypeAddress,
  1674. },
  1675. {
  1676. ID: field2,
  1677. Label: field2,
  1678. Value: value2,
  1679. Type: onepassword.FieldTypeString,
  1680. },
  1681. {
  1682. ID: field3,
  1683. Label: field3,
  1684. Value: value3,
  1685. Type: onepassword.FieldTypeConcealed,
  1686. },
  1687. },
  1688. fieldName: field2,
  1689. newVal: "testing",
  1690. expectedFields: []*onepassword.ItemField{
  1691. {
  1692. ID: field1,
  1693. Label: field1,
  1694. Value: value1,
  1695. Type: onepassword.FieldTypeAddress,
  1696. },
  1697. {
  1698. ID: field2,
  1699. Label: field2,
  1700. Value: "testing",
  1701. Type: onepassword.FieldTypeString,
  1702. },
  1703. {
  1704. ID: field3,
  1705. Label: field3,
  1706. Value: value3,
  1707. Type: onepassword.FieldTypeConcealed,
  1708. },
  1709. },
  1710. },
  1711. {
  1712. setupNote: "add field",
  1713. inputFields: []*onepassword.ItemField{
  1714. {
  1715. ID: field1,
  1716. Value: value1,
  1717. Label: field1,
  1718. Type: onepassword.FieldTypeAddress,
  1719. },
  1720. {
  1721. ID: field2,
  1722. Label: field2,
  1723. Value: value2,
  1724. Type: onepassword.FieldTypeString,
  1725. },
  1726. },
  1727. fieldName: field4,
  1728. newVal: value4,
  1729. expectedFields: []*onepassword.ItemField{
  1730. {
  1731. ID: field1,
  1732. Label: field1,
  1733. Value: value1,
  1734. Type: onepassword.FieldTypeAddress,
  1735. },
  1736. {
  1737. ID: field2,
  1738. Label: field2,
  1739. Value: value2,
  1740. Type: onepassword.FieldTypeString,
  1741. },
  1742. {
  1743. Label: field4,
  1744. Value: value4,
  1745. Type: onepassword.FieldTypeConcealed,
  1746. },
  1747. },
  1748. },
  1749. {
  1750. setupNote: "no changes",
  1751. inputFields: []*onepassword.ItemField{
  1752. {
  1753. ID: field1,
  1754. Label: field1,
  1755. Value: value1,
  1756. Type: onepassword.FieldTypeAddress,
  1757. },
  1758. {
  1759. ID: field2,
  1760. Label: field2,
  1761. Value: value2,
  1762. Type: onepassword.FieldTypeString,
  1763. },
  1764. },
  1765. fieldName: field1,
  1766. newVal: value1,
  1767. expectedErr: nil,
  1768. expectedFields: []*onepassword.ItemField{
  1769. {
  1770. ID: field1,
  1771. Label: field1,
  1772. Value: value1,
  1773. Type: onepassword.FieldTypeAddress,
  1774. },
  1775. {
  1776. ID: field2,
  1777. Label: field2,
  1778. Value: value2,
  1779. Type: onepassword.FieldTypeString,
  1780. },
  1781. },
  1782. },
  1783. {
  1784. setupNote: "multiple fields to remove",
  1785. inputFields: []*onepassword.ItemField{
  1786. {
  1787. ID: field3,
  1788. Label: field3,
  1789. Value: value3,
  1790. Type: onepassword.FieldTypeConcealed,
  1791. },
  1792. {
  1793. ID: field1,
  1794. Label: field1,
  1795. Value: value1,
  1796. Type: onepassword.FieldTypeAddress,
  1797. },
  1798. {
  1799. ID: field3,
  1800. Label: field3,
  1801. Value: value3,
  1802. Type: onepassword.FieldTypeCreditCardType,
  1803. },
  1804. {
  1805. ID: field2,
  1806. Label: field2,
  1807. Value: value2,
  1808. Type: onepassword.FieldTypeString,
  1809. },
  1810. {
  1811. ID: field3,
  1812. Label: field3,
  1813. Value: value3,
  1814. Type: onepassword.FieldTypeGender,
  1815. },
  1816. },
  1817. fieldName: field3,
  1818. expectedErr: ErrExpectedOneField,
  1819. expectedFields: nil,
  1820. },
  1821. }
  1822. // run the tests
  1823. for _, tc := range testCases {
  1824. actualOutput, err := updateFieldValue(tc.inputFields, tc.fieldName, tc.newVal)
  1825. if len(actualOutput) != len(tc.expectedFields) {
  1826. t.Errorf("%s: length fields did not match: -expected, +got:\n-%#v\n+%#v\n", tc.setupNote, tc.expectedFields, actualOutput)
  1827. return
  1828. }
  1829. if !errors.Is(err, tc.expectedErr) {
  1830. t.Errorf(errDoesNotMatchMsgF, tc.setupNote, tc.expectedErr, err)
  1831. }
  1832. for i, check := range tc.expectedFields {
  1833. if len(actualOutput) <= i {
  1834. continue
  1835. }
  1836. if !reflect.DeepEqual(check, actualOutput[i]) {
  1837. t.Errorf("%s: fields at position %d did not match: -expected, +got:\n-%#v\n+%#v\n", tc.setupNote, i, check, actualOutput[i])
  1838. }
  1839. }
  1840. }
  1841. }
  1842. func TestGenerateNewItemField(t *testing.T) {
  1843. field := generateNewItemField("property", "testing")
  1844. if !reflect.DeepEqual(field, &onepassword.ItemField{
  1845. Label: "property",
  1846. Type: onepassword.FieldTypeConcealed,
  1847. Value: "testing",
  1848. }) {
  1849. t.Errorf("field did not match: -expected, +got:\n-%#v\n+%#v\n", &onepassword.ItemField{
  1850. Label: "property",
  1851. Type: onepassword.FieldTypeConcealed,
  1852. Value: "testing",
  1853. }, field)
  1854. }
  1855. }
  1856. func TestProviderOnePasswordPushSecret(t *testing.T) {
  1857. // Most logic is tested in the createItem and updateField functions
  1858. // This test is just to make sure the correct functions are called.
  1859. // the correct values are passed to them, and errors are propagated
  1860. type testCase struct {
  1861. vaults map[string]int
  1862. expectedErr error
  1863. setupNote string
  1864. existingItems []onepassword.Item
  1865. val *corev1.Secret
  1866. existingItemsFields map[string][]*onepassword.ItemField
  1867. createValidateFunc func(*onepassword.Item, string) (*onepassword.Item, error)
  1868. updateValidateFunc func(*onepassword.Item, string) (*onepassword.Item, error)
  1869. ref fakeRef
  1870. }
  1871. var (
  1872. vaultName = "vault1"
  1873. vault = onepassword.Vault{
  1874. ID: vaultName,
  1875. }
  1876. )
  1877. testCases := []testCase{
  1878. {
  1879. vaults: map[string]int{
  1880. vaultName: 1,
  1881. },
  1882. expectedErr: ErrExpectedOneItem,
  1883. setupNote: "find item error",
  1884. existingItems: []onepassword.Item{
  1885. {
  1886. Title: key1,
  1887. }, {
  1888. Title: key1,
  1889. }, // can be empty, testing for error with length
  1890. },
  1891. ref: fakeRef{
  1892. key: key1,
  1893. secretKey: key1,
  1894. },
  1895. val: &corev1.Secret{Data: map[string][]byte{key1: []byte("testing")}},
  1896. },
  1897. {
  1898. setupNote: "create item error",
  1899. expectedErr: ErrNoVaults,
  1900. val: &corev1.Secret{Data: map[string][]byte{key1: []byte("testing")}},
  1901. ref: fakeRef{secretKey: key1},
  1902. vaults: nil,
  1903. },
  1904. {
  1905. setupNote: "key not in data",
  1906. expectedErr: ErrKeyNotFound,
  1907. val: &corev1.Secret{Data: map[string][]byte{}},
  1908. ref: fakeRef{secretKey: key1},
  1909. vaults: nil,
  1910. },
  1911. {
  1912. setupNote: "create item success",
  1913. expectedErr: nil,
  1914. val: &corev1.Secret{Data: map[string][]byte{
  1915. key1: []byte("testing"),
  1916. }},
  1917. ref: fakeRef{
  1918. key: key1,
  1919. prop: "prop",
  1920. secretKey: key1,
  1921. },
  1922. vaults: map[string]int{
  1923. vaultName: 1,
  1924. },
  1925. createValidateFunc: func(item *onepassword.Item, s string) (*onepassword.Item, error) {
  1926. validateItem(t, &onepassword.Item{
  1927. Title: key1,
  1928. Category: onepassword.Server,
  1929. Vault: onepassword.ItemVault{
  1930. ID: vaultName,
  1931. },
  1932. Fields: []*onepassword.ItemField{
  1933. generateNewItemField("prop", "testing"),
  1934. },
  1935. }, item)
  1936. return item, nil
  1937. },
  1938. },
  1939. {
  1940. setupNote: "update fields error",
  1941. expectedErr: ErrExpectedOneField,
  1942. val: &corev1.Secret{Data: map[string][]byte{
  1943. "key2": []byte("testing"),
  1944. }},
  1945. ref: fakeRef{
  1946. key: key1,
  1947. prop: "prop",
  1948. secretKey: "key2",
  1949. },
  1950. vaults: map[string]int{
  1951. vaultName: 1,
  1952. },
  1953. existingItemsFields: map[string][]*onepassword.ItemField{
  1954. key1: {
  1955. {
  1956. Label: "prop",
  1957. },
  1958. {
  1959. Label: "prop",
  1960. },
  1961. },
  1962. },
  1963. existingItems: []onepassword.Item{
  1964. {
  1965. Vault: onepassword.ItemVault{
  1966. ID: vaultName,
  1967. },
  1968. ID: key1,
  1969. Title: key1,
  1970. },
  1971. },
  1972. },
  1973. {
  1974. setupNote: "standard update",
  1975. expectedErr: nil,
  1976. val: &corev1.Secret{Data: map[string][]byte{
  1977. "key3": []byte("testing2"),
  1978. }},
  1979. ref: fakeRef{
  1980. key: key1,
  1981. prop: "",
  1982. secretKey: "key3",
  1983. },
  1984. vaults: map[string]int{
  1985. vaultName: 1,
  1986. },
  1987. existingItemsFields: map[string][]*onepassword.ItemField{
  1988. key1: {
  1989. {
  1990. Label: "not-prop",
  1991. },
  1992. },
  1993. },
  1994. updateValidateFunc: func(item *onepassword.Item, s string) (*onepassword.Item, error) {
  1995. expectedItem := &onepassword.Item{
  1996. Vault: onepassword.ItemVault{
  1997. ID: vaultName,
  1998. },
  1999. ID: key1,
  2000. Title: key1,
  2001. Fields: []*onepassword.ItemField{
  2002. {
  2003. Label: "not-prop",
  2004. },
  2005. {
  2006. Label: "password",
  2007. Value: "testing2",
  2008. Type: onepassword.FieldTypeConcealed,
  2009. },
  2010. },
  2011. }
  2012. validateItem(t, expectedItem, item)
  2013. return expectedItem, nil
  2014. },
  2015. existingItems: []onepassword.Item{
  2016. {
  2017. Vault: onepassword.ItemVault{
  2018. ID: vaultName,
  2019. },
  2020. ID: key1,
  2021. Title: key1,
  2022. },
  2023. },
  2024. },
  2025. }
  2026. provider := &ProviderOnePassword{}
  2027. for _, tc := range testCases {
  2028. t.Run(tc.setupNote, func(t *testing.T) {
  2029. // setup
  2030. mockClient := fake.NewMockClient()
  2031. mockClient.MockVaults = map[string][]onepassword.Vault{
  2032. vaultName: {vault},
  2033. }
  2034. mockClient.MockItems = map[string][]onepassword.Item{
  2035. vaultName: tc.existingItems,
  2036. }
  2037. mockClient.MockItemFields = map[string]map[string][]*onepassword.ItemField{
  2038. vaultName: tc.existingItemsFields,
  2039. }
  2040. mockClient.CreateItemValidateFunc = func(item *onepassword.Item, s string) (*onepassword.Item, error) {
  2041. return tc.createValidateFunc(item, s)
  2042. }
  2043. mockClient.UpdateItemValidateFunc = func(item *onepassword.Item, s string) (*onepassword.Item, error) {
  2044. return tc.updateValidateFunc(item, s)
  2045. }
  2046. provider.client = mockClient
  2047. provider.vaults = tc.vaults
  2048. err := provider.PushSecret(context.Background(), tc.val, tc.ref)
  2049. if !errors.Is(err, tc.expectedErr) {
  2050. t.Errorf(errDoesNotMatchMsgF, tc.setupNote, tc.expectedErr, err)
  2051. }
  2052. })
  2053. }
  2054. }