onepassword_test.go 59 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231
  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. AppendItem(myVaultID, onepassword.Item{
  667. ID: myItemID,
  668. Title: myItem,
  669. Vault: onepassword.ItemVault{ID: myVaultID},
  670. Files: []*onepassword.File{
  671. {
  672. ID: myFilePNGID,
  673. Name: myFilePNG,
  674. },
  675. },
  676. }).
  677. AppendItemField(myVaultID, myItemID, onepassword.ItemField{
  678. Label: password,
  679. Value: value2,
  680. }).
  681. AppendItemField(myVaultID, myItemID, onepassword.ItemField{
  682. Label: key1,
  683. Value: value1,
  684. }).
  685. SetFileContents(myFilePNG, []byte(myContents)),
  686. },
  687. checks: []check{
  688. {
  689. checkNote: key1,
  690. ref: esv1beta1.ExternalSecretDataRemoteRef{
  691. Key: myItem,
  692. Property: key1,
  693. },
  694. expectedValue: value1,
  695. expectedErr: nil,
  696. },
  697. {
  698. checkNote: key1 + " with prefix",
  699. ref: esv1beta1.ExternalSecretDataRemoteRef{
  700. Key: myItem,
  701. Property: fieldPrefix + prefixSplitter + key1,
  702. },
  703. expectedValue: value1,
  704. expectedErr: nil,
  705. },
  706. {
  707. checkNote: "'password' (defaulted property)",
  708. ref: esv1beta1.ExternalSecretDataRemoteRef{
  709. Key: myItem,
  710. },
  711. expectedValue: value2,
  712. expectedErr: nil,
  713. },
  714. {
  715. checkNote: "'ref.version' not implemented",
  716. ref: esv1beta1.ExternalSecretDataRemoteRef{
  717. Key: myItem,
  718. Property: key1,
  719. Version: "123",
  720. },
  721. expectedErr: errors.New(errVersionNotImplemented),
  722. },
  723. {
  724. checkNote: "file named my-file.png with prefix",
  725. ref: esv1beta1.ExternalSecretDataRemoteRef{
  726. Key: myItem,
  727. Property: filePrefix + prefixSplitter + myFilePNG,
  728. },
  729. expectedValue: myContents,
  730. expectedErr: nil,
  731. },
  732. },
  733. },
  734. {
  735. setupNote: "files are loaded",
  736. provider: &ProviderOnePassword{
  737. vaults: map[string]int{myVault: 1},
  738. client: fake.NewMockClient().
  739. AddPredictableVault(myVault).
  740. AppendItem(myVaultID, onepassword.Item{
  741. ID: myItemID,
  742. Title: myItem,
  743. Vault: onepassword.ItemVault{ID: myVaultID},
  744. Category: documentCategory,
  745. Files: []*onepassword.File{
  746. {
  747. ID: myFilePNGID,
  748. Name: myFilePNG,
  749. },
  750. },
  751. }).
  752. AppendItemField(myVaultID, myItemID, onepassword.ItemField{
  753. Label: key1,
  754. Value: value2,
  755. }).
  756. SetFileContents(myFilePNG, []byte(myContents)),
  757. },
  758. checks: []check{
  759. {
  760. checkNote: "field named password",
  761. ref: esv1beta1.ExternalSecretDataRemoteRef{
  762. Key: myItem,
  763. Property: fieldPrefix + prefixSplitter + key1,
  764. },
  765. expectedValue: value2,
  766. expectedErr: nil,
  767. },
  768. {
  769. checkNote: "file named my-file.png",
  770. ref: esv1beta1.ExternalSecretDataRemoteRef{
  771. Key: myItem,
  772. Property: myFilePNG,
  773. },
  774. expectedValue: myContents,
  775. expectedErr: nil,
  776. },
  777. {
  778. checkNote: "file named my-file.png with prefix",
  779. ref: esv1beta1.ExternalSecretDataRemoteRef{
  780. Key: myItem,
  781. Property: filePrefix + prefixSplitter + myFilePNG,
  782. },
  783. expectedValue: myContents,
  784. expectedErr: nil,
  785. },
  786. {
  787. checkNote: "empty ref.Property",
  788. ref: esv1beta1.ExternalSecretDataRemoteRef{
  789. Key: myItem,
  790. },
  791. expectedValue: myContents,
  792. expectedErr: nil,
  793. },
  794. {
  795. checkNote: "file non existent",
  796. ref: esv1beta1.ExternalSecretDataRemoteRef{
  797. Key: myItem,
  798. Property: "you-cant-find-me.png",
  799. },
  800. expectedErr: fmt.Errorf(errDocumentNotFound, errors.New("'my-item', 'you-cant-find-me.png'")),
  801. },
  802. {
  803. checkNote: "file non existent with prefix",
  804. ref: esv1beta1.ExternalSecretDataRemoteRef{
  805. Key: myItem,
  806. Property: "file/you-cant-find-me.png",
  807. },
  808. expectedErr: fmt.Errorf(errDocumentNotFound, errors.New("'my-item', 'you-cant-find-me.png'")),
  809. },
  810. },
  811. },
  812. {
  813. setupNote: "one vault, one item, two fields w/ same Label",
  814. provider: &ProviderOnePassword{
  815. vaults: map[string]int{myVault: 1},
  816. client: fake.NewMockClient().
  817. AddPredictableVault(myVault).
  818. AddPredictableItemWithField(myVault, myItem, key1, value1).
  819. AppendItemField(myVaultID, myItemID, onepassword.ItemField{
  820. Label: key1,
  821. Value: value2,
  822. }),
  823. },
  824. checks: []check{
  825. {
  826. checkNote: key1,
  827. ref: esv1beta1.ExternalSecretDataRemoteRef{
  828. Key: myItem,
  829. Property: key1,
  830. },
  831. expectedErr: fmt.Errorf(errFromErrMsgF, ErrExpectedOneField, "'key1' in 'my-item', got 2"),
  832. },
  833. },
  834. },
  835. }
  836. // run the tests
  837. for _, tc := range testCases {
  838. for _, check := range tc.checks {
  839. got, err := tc.provider.GetSecret(context.Background(), check.ref)
  840. notes := fmt.Sprintf(setupCheckFormat, tc.setupNote, check.checkNote)
  841. if check.expectedErr == nil && err != nil {
  842. // expected no error, got one
  843. t.Errorf(getSecretErrFormat, notes, nil, err)
  844. }
  845. if check.expectedErr != nil && err == nil {
  846. // expected an error, didn't get one
  847. t.Errorf(getSecretErrFormat, notes, check.expectedErr.Error(), nil)
  848. }
  849. if check.expectedErr != nil && err != nil && err.Error() != check.expectedErr.Error() {
  850. // expected an error, got the wrong one
  851. t.Errorf(getSecretErrFormat, notes, check.expectedErr.Error(), err.Error())
  852. }
  853. if check.expectedValue != "" {
  854. if check.expectedValue != string(got) {
  855. // expected a predefined value, got something else
  856. t.Errorf(getSecretErrFormat, notes, check.expectedValue, string(got))
  857. }
  858. }
  859. }
  860. }
  861. }
  862. // most functionality is tested in TestFindItem. here we just check:
  863. //
  864. // all keys are fetched and the map is compiled correctly,
  865. // files are loaded, and the data or errors are properly returned.
  866. func TestGetSecretMap(t *testing.T) {
  867. type check struct {
  868. checkNote string
  869. ref esv1beta1.ExternalSecretDataRemoteRef
  870. expectedMap map[string][]byte
  871. expectedErr error
  872. }
  873. type testCase struct {
  874. setupNote string
  875. provider *ProviderOnePassword
  876. checks []check
  877. }
  878. testCases := []testCase{
  879. {
  880. setupNote: "one vault, one item, two fields",
  881. provider: &ProviderOnePassword{
  882. vaults: map[string]int{myVault: 1},
  883. client: fake.NewMockClient().
  884. AddPredictableVault(myVault).
  885. AppendItem(myVaultID, onepassword.Item{
  886. ID: myItemID,
  887. Title: myItem,
  888. Vault: onepassword.ItemVault{ID: myVaultID},
  889. Files: []*onepassword.File{
  890. {
  891. ID: myFilePNGID,
  892. Name: myFilePNG,
  893. },
  894. {
  895. ID: myFile2ID,
  896. Name: myFile2PNG,
  897. },
  898. },
  899. }).
  900. AppendItemField(myVaultID, myItemID, onepassword.ItemField{
  901. Label: key1,
  902. Value: value1,
  903. }).
  904. AppendItemField(myVaultID, myItemID, onepassword.ItemField{
  905. Label: password,
  906. Value: value2,
  907. }).
  908. SetFileContents(myFilePNG, []byte(myContents)).
  909. SetFileContents(myFile2PNG, []byte(myContents2)),
  910. },
  911. checks: []check{
  912. {
  913. checkNote: "all Properties",
  914. ref: esv1beta1.ExternalSecretDataRemoteRef{
  915. Key: myItem,
  916. },
  917. expectedMap: map[string][]byte{
  918. key1: []byte(value1),
  919. password: []byte(value2),
  920. },
  921. expectedErr: nil,
  922. },
  923. {
  924. checkNote: "limit by Property",
  925. ref: esv1beta1.ExternalSecretDataRemoteRef{
  926. Key: myItem,
  927. Property: password,
  928. },
  929. expectedMap: map[string][]byte{
  930. password: []byte(value2),
  931. },
  932. expectedErr: nil,
  933. },
  934. {
  935. checkNote: "'ref.version' not implemented",
  936. ref: esv1beta1.ExternalSecretDataRemoteRef{
  937. Key: myItem,
  938. Property: key1,
  939. Version: "123",
  940. },
  941. expectedErr: errors.New(errVersionNotImplemented),
  942. },
  943. {
  944. checkNote: "limit by Property with prefix",
  945. ref: esv1beta1.ExternalSecretDataRemoteRef{
  946. Key: myItem,
  947. Property: filePrefix + prefixSplitter + myFilePNG,
  948. },
  949. expectedMap: map[string][]byte{
  950. myFilePNG: []byte(myContents),
  951. },
  952. expectedErr: nil,
  953. },
  954. },
  955. },
  956. {
  957. setupNote: "files",
  958. provider: &ProviderOnePassword{
  959. vaults: map[string]int{myVault: 1},
  960. client: fake.NewMockClient().
  961. AddPredictableVault(myVault).
  962. AppendItem(myVaultID, onepassword.Item{
  963. ID: myItemID,
  964. Title: myItem,
  965. Vault: onepassword.ItemVault{ID: myVaultID},
  966. Category: documentCategory,
  967. Files: []*onepassword.File{
  968. {
  969. ID: myFilePNGID,
  970. Name: myFilePNG,
  971. },
  972. {
  973. ID: myFile2ID,
  974. Name: myFile2PNG,
  975. },
  976. },
  977. }).
  978. AppendItemField(myVaultID, myItemID, onepassword.ItemField{
  979. Label: key1,
  980. Value: value2,
  981. }).
  982. SetFileContents(myFilePNG, []byte(myContents)).
  983. SetFileContents(myFile2PNG, []byte(myContents2)),
  984. },
  985. checks: []check{
  986. {
  987. checkNote: "all Properties",
  988. ref: esv1beta1.ExternalSecretDataRemoteRef{
  989. Key: myItem,
  990. },
  991. expectedMap: map[string][]byte{
  992. myFilePNG: []byte(myContents),
  993. myFile2PNG: []byte(myContents2),
  994. },
  995. expectedErr: nil,
  996. },
  997. {
  998. checkNote: "limit by Property",
  999. ref: esv1beta1.ExternalSecretDataRemoteRef{
  1000. Key: myItem,
  1001. Property: myFilePNG,
  1002. },
  1003. expectedMap: map[string][]byte{
  1004. myFilePNG: []byte(myContents),
  1005. },
  1006. expectedErr: nil,
  1007. },
  1008. {
  1009. checkNote: "limit by Property with prefix",
  1010. ref: esv1beta1.ExternalSecretDataRemoteRef{
  1011. Key: myItem,
  1012. Property: filePrefix + prefixSplitter + myFilePNG,
  1013. },
  1014. expectedMap: map[string][]byte{
  1015. myFilePNG: []byte(myContents),
  1016. },
  1017. expectedErr: nil,
  1018. },
  1019. {
  1020. checkNote: "get field limit by Property",
  1021. ref: esv1beta1.ExternalSecretDataRemoteRef{
  1022. Key: myItem,
  1023. Property: fieldPrefix + prefixSplitter + key1,
  1024. },
  1025. expectedMap: map[string][]byte{
  1026. key1: []byte(value2),
  1027. },
  1028. expectedErr: nil,
  1029. },
  1030. },
  1031. },
  1032. {
  1033. setupNote: "one vault, one item, two fields w/ same Label",
  1034. provider: &ProviderOnePassword{
  1035. vaults: map[string]int{myVault: 1},
  1036. client: fake.NewMockClient().
  1037. AddPredictableVault(myVault).
  1038. AddPredictableItemWithField(myVault, myItem, key1, value1).
  1039. AppendItemField(myVaultID, myItemID, onepassword.ItemField{
  1040. Label: key1,
  1041. Value: value2,
  1042. }),
  1043. },
  1044. checks: []check{
  1045. {
  1046. checkNote: key1,
  1047. ref: esv1beta1.ExternalSecretDataRemoteRef{
  1048. Key: myItem,
  1049. },
  1050. expectedMap: nil,
  1051. expectedErr: fmt.Errorf(errFromErrMsgF, ErrExpectedOneField, "'key1' in 'my-item', got 2"),
  1052. },
  1053. },
  1054. },
  1055. }
  1056. // run the tests
  1057. for _, tc := range testCases {
  1058. for _, check := range tc.checks {
  1059. gotMap, err := tc.provider.GetSecretMap(context.Background(), check.ref)
  1060. notes := fmt.Sprintf(setupCheckFormat, tc.setupNote, check.checkNote)
  1061. if check.expectedErr == nil && err != nil {
  1062. // expected no error, got one
  1063. t.Errorf(getSecretMapErrFormat, notes, nil, err)
  1064. }
  1065. if check.expectedErr != nil && err == nil {
  1066. // expected an error, didn't get one
  1067. t.Errorf(getSecretMapErrFormat, notes, check.expectedErr.Error(), nil)
  1068. }
  1069. if check.expectedErr != nil && err != nil && err.Error() != check.expectedErr.Error() {
  1070. // expected an error, got the wrong one
  1071. t.Errorf(getSecretMapErrFormat, notes, check.expectedErr.Error(), err.Error())
  1072. }
  1073. if !reflect.DeepEqual(check.expectedMap, gotMap) {
  1074. // expected a predefined map, got something else
  1075. t.Errorf(getSecretMapErrFormat, notes, check.expectedMap, gotMap)
  1076. }
  1077. }
  1078. }
  1079. }
  1080. func TestGetAllSecrets(t *testing.T) {
  1081. type check struct {
  1082. checkNote string
  1083. ref esv1beta1.ExternalSecretFind
  1084. expectedMap map[string][]byte
  1085. expectedErr error
  1086. }
  1087. type testCase struct {
  1088. setupNote string
  1089. provider *ProviderOnePassword
  1090. checks []check
  1091. }
  1092. testCases := []testCase{
  1093. {
  1094. setupNote: "three vaults, three items, all different field Labels",
  1095. provider: &ProviderOnePassword{
  1096. vaults: map[string]int{myVault: 1, myOtherVault: 2, myNonMatchingVault: 3},
  1097. client: fake.NewMockClient().
  1098. AddPredictableVault(myVault).
  1099. AddPredictableItemWithField(myVault, myItem, key1, value1).
  1100. AppendItemField(myVaultID, myItemID, onepassword.ItemField{
  1101. Label: key2,
  1102. Value: value2,
  1103. }).
  1104. AddPredictableVault(myOtherVault).
  1105. AddPredictableItemWithField(myOtherVault, myOtherItem, key3, value3).
  1106. AppendItemField(myOtherVaultID, myOtherItemID, onepassword.ItemField{
  1107. Label: key4,
  1108. Value: value4,
  1109. }).
  1110. AddPredictableVault(myNonMatchingVault).
  1111. AddPredictableItemWithField(myNonMatchingVault, myNonMatchingItem, "non-matching5", "value5").
  1112. AppendItemField(myNonMatchingVaultID, myNonMatchingItemID, onepassword.ItemField{
  1113. Label: "non-matching6",
  1114. Value: "value6",
  1115. }),
  1116. },
  1117. checks: []check{
  1118. {
  1119. checkNote: "find some with path only",
  1120. ref: esv1beta1.ExternalSecretFind{
  1121. Path: pointer.To(myItem),
  1122. },
  1123. expectedMap: map[string][]byte{
  1124. key1: []byte(value1),
  1125. key2: []byte(value2),
  1126. },
  1127. expectedErr: nil,
  1128. },
  1129. {
  1130. checkNote: "find most with regex 'key*'",
  1131. ref: esv1beta1.ExternalSecretFind{
  1132. Name: &esv1beta1.FindName{
  1133. RegExp: "key*",
  1134. },
  1135. },
  1136. expectedMap: map[string][]byte{
  1137. key1: []byte(value1),
  1138. key2: []byte(value2),
  1139. key3: []byte(value3),
  1140. key4: []byte(value4),
  1141. },
  1142. expectedErr: nil,
  1143. },
  1144. {
  1145. checkNote: "find some with regex 'key*' and path 'my-other-item'",
  1146. ref: esv1beta1.ExternalSecretFind{
  1147. Name: &esv1beta1.FindName{
  1148. RegExp: "key*",
  1149. },
  1150. Path: pointer.To(myOtherItem),
  1151. },
  1152. expectedMap: map[string][]byte{
  1153. key3: []byte(value3),
  1154. key4: []byte(value4),
  1155. },
  1156. expectedErr: nil,
  1157. },
  1158. {
  1159. checkNote: "find none with regex 'asdf*'",
  1160. ref: esv1beta1.ExternalSecretFind{
  1161. Name: &esv1beta1.FindName{
  1162. RegExp: "asdf*",
  1163. },
  1164. },
  1165. expectedMap: map[string][]byte{},
  1166. expectedErr: nil,
  1167. },
  1168. {
  1169. checkNote: "find none with path 'no-exist'",
  1170. ref: esv1beta1.ExternalSecretFind{
  1171. Name: &esv1beta1.FindName{
  1172. RegExp: "key*",
  1173. },
  1174. Path: pointer.To("no-exist"),
  1175. },
  1176. expectedMap: map[string][]byte{},
  1177. expectedErr: nil,
  1178. },
  1179. {
  1180. checkNote: "error when find.tags",
  1181. ref: esv1beta1.ExternalSecretFind{
  1182. Name: &esv1beta1.FindName{
  1183. RegExp: "key*",
  1184. },
  1185. Tags: map[string]string{
  1186. "asdf": "fdas",
  1187. },
  1188. },
  1189. expectedErr: errors.New(errTagsNotImplemented),
  1190. },
  1191. },
  1192. },
  1193. {
  1194. setupNote: "3 vaults, 4 items, 5 files",
  1195. provider: &ProviderOnePassword{
  1196. vaults: map[string]int{myVault: 1, myOtherVault: 2, myNonMatchingVault: 3},
  1197. client: fake.NewMockClient().
  1198. // my-vault
  1199. AddPredictableVault(myVault).
  1200. AppendItem(myVaultID, onepassword.Item{
  1201. ID: myItemID,
  1202. Title: myItem,
  1203. Vault: onepassword.ItemVault{ID: myVaultID},
  1204. Category: documentCategory,
  1205. Files: []*onepassword.File{
  1206. {
  1207. ID: myFilePNGID,
  1208. Name: myFilePNG,
  1209. },
  1210. {
  1211. ID: mySecondFileTXTID,
  1212. Name: mySecondFileTXT,
  1213. },
  1214. },
  1215. }).
  1216. SetFileContents(myFilePNG, []byte(myContents)).
  1217. SetFileContents(mySecondFileTXT, []byte(mySecondContents)).
  1218. AppendItem(myVaultID, onepassword.Item{
  1219. ID: "my-item-2-id",
  1220. Title: "my-item-2",
  1221. Vault: onepassword.ItemVault{ID: myVaultID},
  1222. Category: documentCategory,
  1223. Files: []*onepassword.File{
  1224. {
  1225. ID: myFile2ID,
  1226. Name: myFile2TXT,
  1227. },
  1228. },
  1229. }).
  1230. SetFileContents(myFile2TXT, []byte(myContents2)).
  1231. // my-other-vault
  1232. AddPredictableVault(myOtherVault).
  1233. AppendItem(myOtherVaultID, onepassword.Item{
  1234. ID: myOtherItemID,
  1235. Title: myOtherItem,
  1236. Vault: onepassword.ItemVault{ID: myOtherVaultID},
  1237. Category: documentCategory,
  1238. Files: []*onepassword.File{
  1239. {
  1240. ID: myOtherFilePNGID,
  1241. Name: myOtherFilePNG,
  1242. },
  1243. },
  1244. }).
  1245. SetFileContents(myOtherFilePNG, []byte(myOtherContents)).
  1246. // my-non-matching-vault
  1247. AddPredictableVault(myNonMatchingVault).
  1248. AppendItem(myNonMatchingVaultID, onepassword.Item{
  1249. ID: myNonMatchingItemID,
  1250. Title: myNonMatchingItem,
  1251. Vault: onepassword.ItemVault{ID: myNonMatchingVaultID},
  1252. Category: documentCategory,
  1253. Files: []*onepassword.File{
  1254. {
  1255. ID: nonMatchingFilePNGID,
  1256. Name: nonMatchingFilePNG,
  1257. },
  1258. },
  1259. }).
  1260. SetFileContents(nonMatchingFilePNG, []byte(nonMatchingContents)),
  1261. },
  1262. checks: []check{
  1263. {
  1264. checkNote: "find most with regex '^my-*'",
  1265. ref: esv1beta1.ExternalSecretFind{
  1266. Name: &esv1beta1.FindName{
  1267. RegExp: "^my-*",
  1268. },
  1269. },
  1270. expectedMap: map[string][]byte{
  1271. myFilePNG: []byte(myContents),
  1272. mySecondFileTXT: []byte(mySecondContents),
  1273. myFile2TXT: []byte(myContents2),
  1274. myOtherFilePNG: []byte(myOtherContents),
  1275. },
  1276. expectedErr: nil,
  1277. },
  1278. {
  1279. checkNote: "find some with regex '^my-*' and path 'my-other-item'",
  1280. ref: esv1beta1.ExternalSecretFind{
  1281. Name: &esv1beta1.FindName{
  1282. RegExp: "^my-*",
  1283. },
  1284. Path: pointer.To(myOtherItem),
  1285. },
  1286. expectedMap: map[string][]byte{
  1287. myOtherFilePNG: []byte(myOtherContents),
  1288. },
  1289. expectedErr: nil,
  1290. },
  1291. {
  1292. checkNote: "find none with regex '^asdf*'",
  1293. ref: esv1beta1.ExternalSecretFind{
  1294. Name: &esv1beta1.FindName{
  1295. RegExp: "^asdf*",
  1296. },
  1297. },
  1298. expectedMap: map[string][]byte{},
  1299. expectedErr: nil,
  1300. },
  1301. {
  1302. checkNote: "find none with path 'no-exist'",
  1303. ref: esv1beta1.ExternalSecretFind{
  1304. Name: &esv1beta1.FindName{
  1305. RegExp: "^my-*",
  1306. },
  1307. Path: pointer.To("no-exist"),
  1308. },
  1309. expectedMap: map[string][]byte{},
  1310. expectedErr: nil,
  1311. },
  1312. },
  1313. },
  1314. {
  1315. setupNote: "two fields/files with same name, first one wins",
  1316. provider: &ProviderOnePassword{
  1317. vaults: map[string]int{myVault: 1, myOtherVault: 2},
  1318. client: fake.NewMockClient().
  1319. // my-vault
  1320. AddPredictableVault(myVault).
  1321. AddPredictableItemWithField(myVault, myItem, key1, value1).
  1322. AddPredictableItemWithField(myVault, "my-second-item", key1, "value-second").
  1323. AppendItem(myVaultID, onepassword.Item{
  1324. ID: "file-item-id",
  1325. Title: "file-item",
  1326. Vault: onepassword.ItemVault{ID: myVaultID},
  1327. Category: documentCategory,
  1328. Files: []*onepassword.File{
  1329. {
  1330. ID: filePNGID,
  1331. Name: filePNG,
  1332. },
  1333. },
  1334. }).
  1335. SetFileContents(filePNG, []byte(myContents)).
  1336. AppendItem(myVaultID, onepassword.Item{
  1337. ID: "file-item-2-id",
  1338. Title: "file-item-2",
  1339. Vault: onepassword.ItemVault{ID: myVaultID},
  1340. Category: documentCategory,
  1341. Files: []*onepassword.File{
  1342. {
  1343. ID: "file-2-id",
  1344. Name: filePNG,
  1345. },
  1346. },
  1347. }).
  1348. // my-other-vault
  1349. AddPredictableVault(myOtherVault).
  1350. AddPredictableItemWithField(myOtherVault, myOtherItem, key1, "value-other").
  1351. AppendItem(myOtherVaultID, onepassword.Item{
  1352. ID: "file-item-other-id",
  1353. Title: "file-item-other",
  1354. Vault: onepassword.ItemVault{ID: myOtherVaultID},
  1355. Category: documentCategory,
  1356. Files: []*onepassword.File{
  1357. {
  1358. ID: "other-file-id",
  1359. Name: filePNG,
  1360. },
  1361. },
  1362. }),
  1363. },
  1364. checks: []check{
  1365. {
  1366. checkNote: "find fields with regex '^key*'",
  1367. ref: esv1beta1.ExternalSecretFind{
  1368. Name: &esv1beta1.FindName{
  1369. RegExp: "^key*",
  1370. },
  1371. },
  1372. expectedMap: map[string][]byte{
  1373. key1: []byte(value1),
  1374. },
  1375. expectedErr: nil,
  1376. },
  1377. {
  1378. checkNote: "find files with regex '^file*item*'",
  1379. ref: esv1beta1.ExternalSecretFind{
  1380. Name: &esv1beta1.FindName{
  1381. RegExp: "^file*",
  1382. },
  1383. },
  1384. expectedMap: map[string][]byte{
  1385. filePNG: []byte(myContents),
  1386. },
  1387. expectedErr: nil,
  1388. },
  1389. },
  1390. },
  1391. }
  1392. // run the tests
  1393. for _, tc := range testCases {
  1394. for _, check := range tc.checks {
  1395. gotMap, err := tc.provider.GetAllSecrets(context.Background(), check.ref)
  1396. notes := fmt.Sprintf(setupCheckFormat, tc.setupNote, check.checkNote)
  1397. if check.expectedErr == nil && err != nil {
  1398. // expected no error, got one
  1399. t.Fatalf(getAllSecretsErrFormat, notes, nil, err)
  1400. }
  1401. if check.expectedErr != nil && err == nil {
  1402. // expected an error, didn't get one
  1403. t.Errorf(getAllSecretsErrFormat, notes, check.expectedErr.Error(), nil)
  1404. }
  1405. if check.expectedErr != nil && err != nil && err.Error() != check.expectedErr.Error() {
  1406. // expected an error, got the wrong one
  1407. t.Errorf(getAllSecretsErrFormat, notes, check.expectedErr.Error(), err.Error())
  1408. }
  1409. if !reflect.DeepEqual(check.expectedMap, gotMap) {
  1410. // expected a predefined map, got something else
  1411. t.Errorf(getAllSecretsErrFormat, notes, check.expectedMap, gotMap)
  1412. }
  1413. }
  1414. }
  1415. }
  1416. func TestSortVaults(t *testing.T) {
  1417. type testCase struct {
  1418. vaults map[string]int
  1419. expected []string
  1420. }
  1421. testCases := []testCase{
  1422. {
  1423. vaults: map[string]int{
  1424. one: 1,
  1425. three: 3,
  1426. two: 2,
  1427. },
  1428. expected: []string{
  1429. one,
  1430. two,
  1431. three,
  1432. },
  1433. },
  1434. {
  1435. vaults: map[string]int{
  1436. "four": 100,
  1437. one: 1,
  1438. three: 3,
  1439. two: 2,
  1440. },
  1441. expected: []string{
  1442. one,
  1443. two,
  1444. three,
  1445. "four",
  1446. },
  1447. },
  1448. }
  1449. // run the tests
  1450. for _, tc := range testCases {
  1451. got := sortVaults(tc.vaults)
  1452. if !reflect.DeepEqual(got, tc.expected) {
  1453. t.Errorf("onepassword.sortVaults(...): -expected, +got:\n-%#v\n+%#v\n", tc.expected, got)
  1454. }
  1455. }
  1456. }
  1457. func TestHasUniqueVaultNumbers(t *testing.T) {
  1458. type testCase struct {
  1459. vaults map[string]int
  1460. expected bool
  1461. }
  1462. testCases := []testCase{
  1463. {
  1464. vaults: map[string]int{
  1465. one: 1,
  1466. three: 3,
  1467. two: 2,
  1468. },
  1469. expected: true,
  1470. },
  1471. {
  1472. vaults: map[string]int{
  1473. "four": 100,
  1474. one: 1,
  1475. three: 3,
  1476. two: 2,
  1477. "eight": 100,
  1478. },
  1479. expected: false,
  1480. },
  1481. {
  1482. vaults: map[string]int{
  1483. one: 1,
  1484. "1": 1,
  1485. three: 3,
  1486. two: 2,
  1487. },
  1488. expected: false,
  1489. },
  1490. }
  1491. // run the tests
  1492. for _, tc := range testCases {
  1493. got := hasUniqueVaultNumbers(tc.vaults)
  1494. if got != tc.expected {
  1495. t.Errorf("onepassword.hasUniqueVaultNumbers(...): -expected, +got:\n-%#v\n+%#v\n", tc.expected, got)
  1496. }
  1497. }
  1498. }
  1499. type fakeRef struct {
  1500. key string
  1501. prop string
  1502. secretKey string
  1503. }
  1504. func (f fakeRef) GetRemoteKey() string {
  1505. return f.key
  1506. }
  1507. func (f fakeRef) GetProperty() string {
  1508. return f.prop
  1509. }
  1510. func (f fakeRef) GetSecretKey() string {
  1511. return f.secretKey
  1512. }
  1513. func (f fakeRef) GetMetadata() *apiextensionsv1.JSON {
  1514. return nil
  1515. }
  1516. func validateItem(t *testing.T, expectedItem, actualItem *onepassword.Item) {
  1517. t.Helper()
  1518. if !reflect.DeepEqual(expectedItem, actualItem) {
  1519. t.Errorf("expected item %v, got %v", expectedItem, actualItem)
  1520. }
  1521. }
  1522. func TestProviderOnePasswordCreateItem(t *testing.T) {
  1523. type testCase struct {
  1524. vaults map[string]int
  1525. expectedErr error
  1526. setupNote string
  1527. val []byte
  1528. createValidateFunc func(*testing.T, *onepassword.Item, string) (*onepassword.Item, error)
  1529. ref esv1beta1.PushSecretData
  1530. }
  1531. const vaultName = "vault1"
  1532. thridPartyErr := errors.New("third party error")
  1533. testCases := []testCase{
  1534. {
  1535. setupNote: "standard create",
  1536. val: []byte("value"),
  1537. ref: fakeRef{
  1538. key: "testing",
  1539. prop: "prop",
  1540. },
  1541. expectedErr: nil,
  1542. vaults: map[string]int{
  1543. vaultName: 1,
  1544. },
  1545. createValidateFunc: func(t *testing.T, item *onepassword.Item, s string) (*onepassword.Item, error) {
  1546. validateItem(t, &onepassword.Item{
  1547. Title: "testing",
  1548. Category: onepassword.Server,
  1549. Vault: onepassword.ItemVault{
  1550. ID: vaultName,
  1551. },
  1552. Fields: []*onepassword.ItemField{
  1553. generateNewItemField("prop", "value"),
  1554. },
  1555. }, item)
  1556. return item, nil
  1557. },
  1558. },
  1559. {
  1560. setupNote: "standard create with no property",
  1561. val: []byte("value2"),
  1562. ref: fakeRef{
  1563. key: "testing2",
  1564. prop: "",
  1565. },
  1566. vaults: map[string]int{
  1567. vaultName: 2,
  1568. },
  1569. createValidateFunc: func(t *testing.T, item *onepassword.Item, s string) (*onepassword.Item, error) {
  1570. validateItem(t, &onepassword.Item{
  1571. Title: "testing2",
  1572. Category: onepassword.Server,
  1573. Vault: onepassword.ItemVault{
  1574. ID: vaultName,
  1575. },
  1576. Fields: []*onepassword.ItemField{
  1577. generateNewItemField("password", "value2"),
  1578. },
  1579. }, item)
  1580. return item, nil
  1581. },
  1582. },
  1583. {
  1584. setupNote: "no vaults",
  1585. val: []byte("value"),
  1586. ref: fakeRef{
  1587. key: "testing",
  1588. prop: "prop",
  1589. },
  1590. vaults: map[string]int{},
  1591. expectedErr: ErrNoVaults,
  1592. createValidateFunc: func(t *testing.T, item *onepassword.Item, s string) (*onepassword.Item, error) {
  1593. t.Errorf("onepassword.createItem(...): should not have been called")
  1594. return nil, nil
  1595. },
  1596. },
  1597. {
  1598. setupNote: "error on create",
  1599. val: []byte("testing"),
  1600. ref: fakeRef{
  1601. key: "another",
  1602. prop: "property",
  1603. },
  1604. vaults: map[string]int{
  1605. vaultName: 1,
  1606. },
  1607. expectedErr: thridPartyErr,
  1608. createValidateFunc: func(t *testing.T, item *onepassword.Item, s string) (*onepassword.Item, error) {
  1609. validateItem(t, &onepassword.Item{
  1610. Title: "another",
  1611. Category: onepassword.Server,
  1612. Vault: onepassword.ItemVault{
  1613. ID: vaultName,
  1614. },
  1615. Fields: []*onepassword.ItemField{
  1616. generateNewItemField("property", "testing"),
  1617. },
  1618. }, item)
  1619. return nil, thridPartyErr
  1620. },
  1621. },
  1622. }
  1623. provider := &ProviderOnePassword{}
  1624. for _, tc := range testCases {
  1625. // setup
  1626. mockClient := fake.NewMockClient()
  1627. mockClient.CreateItemValidateFunc = func(item *onepassword.Item, s string) (*onepassword.Item, error) {
  1628. i, e := tc.createValidateFunc(t, item, s)
  1629. return i, e
  1630. }
  1631. provider.client = mockClient
  1632. provider.vaults = tc.vaults
  1633. err := provider.createItem(tc.val, tc.ref)
  1634. if !errors.Is(err, tc.expectedErr) {
  1635. t.Errorf(errDoesNotMatchMsgF, tc.setupNote, tc.expectedErr, err)
  1636. }
  1637. }
  1638. }
  1639. func TestProviderOnePasswordDeleteItem(t *testing.T) {
  1640. type testCase struct {
  1641. inputFields []*onepassword.ItemField
  1642. fieldName string
  1643. expectedErr error
  1644. expectedFields []*onepassword.ItemField
  1645. setupNote string
  1646. }
  1647. field1, field2, field3, field4 := "field1", "field2", "field3", "field4"
  1648. testCases := []testCase{
  1649. {
  1650. setupNote: "one field to remove",
  1651. inputFields: []*onepassword.ItemField{
  1652. {
  1653. ID: field1,
  1654. Label: field1,
  1655. Type: onepassword.FieldTypeAddress,
  1656. },
  1657. {
  1658. ID: field2,
  1659. Label: field2,
  1660. Type: onepassword.FieldTypeString,
  1661. },
  1662. {
  1663. ID: field3,
  1664. Label: field3,
  1665. Type: onepassword.FieldTypeConcealed,
  1666. },
  1667. },
  1668. fieldName: field2,
  1669. expectedFields: []*onepassword.ItemField{
  1670. {
  1671. ID: field1,
  1672. Label: field1,
  1673. Type: onepassword.FieldTypeAddress,
  1674. },
  1675. {
  1676. ID: field3,
  1677. Label: field3,
  1678. Type: onepassword.FieldTypeConcealed,
  1679. },
  1680. },
  1681. },
  1682. {
  1683. setupNote: "no fields to remove",
  1684. inputFields: []*onepassword.ItemField{
  1685. {
  1686. ID: field1,
  1687. Label: field1,
  1688. Type: onepassword.FieldTypeAddress,
  1689. },
  1690. {
  1691. ID: field2,
  1692. Label: field2,
  1693. Type: onepassword.FieldTypeString,
  1694. },
  1695. {
  1696. ID: field3,
  1697. Label: field3,
  1698. Type: onepassword.FieldTypeConcealed,
  1699. },
  1700. },
  1701. expectedErr: nil,
  1702. fieldName: field4,
  1703. expectedFields: []*onepassword.ItemField{
  1704. {
  1705. ID: field1,
  1706. Label: field1,
  1707. Type: onepassword.FieldTypeAddress,
  1708. },
  1709. {
  1710. ID: field2,
  1711. Label: field2,
  1712. Type: onepassword.FieldTypeString,
  1713. },
  1714. {
  1715. ID: field3,
  1716. Label: field3,
  1717. Type: onepassword.FieldTypeConcealed,
  1718. },
  1719. },
  1720. },
  1721. {
  1722. setupNote: "multiple fields to remove",
  1723. inputFields: []*onepassword.ItemField{
  1724. {
  1725. ID: field3,
  1726. Label: field3,
  1727. Type: onepassword.FieldTypeConcealed,
  1728. },
  1729. {
  1730. ID: field1,
  1731. Label: field1,
  1732. Type: onepassword.FieldTypeAddress,
  1733. },
  1734. {
  1735. ID: field3,
  1736. Label: field3,
  1737. Type: onepassword.FieldTypeCreditCardType,
  1738. },
  1739. {
  1740. ID: field2,
  1741. Label: field2,
  1742. Type: onepassword.FieldTypeString,
  1743. },
  1744. {
  1745. ID: field3,
  1746. Label: field3,
  1747. Type: onepassword.FieldTypeGender,
  1748. },
  1749. },
  1750. fieldName: field3,
  1751. expectedErr: ErrExpectedOneField,
  1752. expectedFields: nil,
  1753. },
  1754. }
  1755. // run the tests
  1756. for _, tc := range testCases {
  1757. actualOutput, err := deleteField(tc.inputFields, tc.fieldName)
  1758. if len(actualOutput) != len(tc.expectedFields) {
  1759. t.Errorf("%s: length fields did not match: -expected, +got:\n-%#v\n+%#v\n", tc.setupNote, tc.expectedFields, actualOutput)
  1760. return
  1761. }
  1762. if !errors.Is(err, tc.expectedErr) {
  1763. t.Errorf(errDoesNotMatchMsgF, tc.setupNote, tc.expectedErr, err)
  1764. }
  1765. for i, check := range tc.expectedFields {
  1766. if len(actualOutput) <= i {
  1767. continue
  1768. }
  1769. if !reflect.DeepEqual(check, actualOutput[i]) {
  1770. t.Errorf("%s: fields at position %d did not match: -expected, +got:\n-%#v\n+%#v\n", tc.setupNote, i, check, actualOutput[i])
  1771. }
  1772. }
  1773. }
  1774. }
  1775. func TestUpdateFields(t *testing.T) {
  1776. type testCase struct {
  1777. inputFields []*onepassword.ItemField
  1778. fieldName string
  1779. newVal string
  1780. expectedErr error
  1781. expectedFields []*onepassword.ItemField
  1782. setupNote string
  1783. }
  1784. field1, field2, field3, field4 := "field1", "field2", "field3", "field4"
  1785. testCases := []testCase{
  1786. {
  1787. setupNote: "one field to update",
  1788. inputFields: []*onepassword.ItemField{
  1789. {
  1790. ID: field1,
  1791. Label: field1,
  1792. Value: value1,
  1793. Type: onepassword.FieldTypeAddress,
  1794. },
  1795. {
  1796. ID: field2,
  1797. Label: field2,
  1798. Value: value2,
  1799. Type: onepassword.FieldTypeString,
  1800. },
  1801. {
  1802. ID: field3,
  1803. Label: field3,
  1804. Value: value3,
  1805. Type: onepassword.FieldTypeConcealed,
  1806. },
  1807. },
  1808. fieldName: field2,
  1809. newVal: "testing",
  1810. expectedFields: []*onepassword.ItemField{
  1811. {
  1812. ID: field1,
  1813. Label: field1,
  1814. Value: value1,
  1815. Type: onepassword.FieldTypeAddress,
  1816. },
  1817. {
  1818. ID: field2,
  1819. Label: field2,
  1820. Value: "testing",
  1821. Type: onepassword.FieldTypeString,
  1822. },
  1823. {
  1824. ID: field3,
  1825. Label: field3,
  1826. Value: value3,
  1827. Type: onepassword.FieldTypeConcealed,
  1828. },
  1829. },
  1830. },
  1831. {
  1832. setupNote: "add field",
  1833. inputFields: []*onepassword.ItemField{
  1834. {
  1835. ID: field1,
  1836. Value: value1,
  1837. Label: field1,
  1838. Type: onepassword.FieldTypeAddress,
  1839. },
  1840. {
  1841. ID: field2,
  1842. Label: field2,
  1843. Value: value2,
  1844. Type: onepassword.FieldTypeString,
  1845. },
  1846. },
  1847. fieldName: field4,
  1848. newVal: value4,
  1849. expectedFields: []*onepassword.ItemField{
  1850. {
  1851. ID: field1,
  1852. Label: field1,
  1853. Value: value1,
  1854. Type: onepassword.FieldTypeAddress,
  1855. },
  1856. {
  1857. ID: field2,
  1858. Label: field2,
  1859. Value: value2,
  1860. Type: onepassword.FieldTypeString,
  1861. },
  1862. {
  1863. Label: field4,
  1864. Value: value4,
  1865. Type: onepassword.FieldTypeConcealed,
  1866. },
  1867. },
  1868. },
  1869. {
  1870. setupNote: "no changes",
  1871. inputFields: []*onepassword.ItemField{
  1872. {
  1873. ID: field1,
  1874. Label: field1,
  1875. Value: value1,
  1876. Type: onepassword.FieldTypeAddress,
  1877. },
  1878. {
  1879. ID: field2,
  1880. Label: field2,
  1881. Value: value2,
  1882. Type: onepassword.FieldTypeString,
  1883. },
  1884. },
  1885. fieldName: field1,
  1886. newVal: value1,
  1887. expectedErr: nil,
  1888. expectedFields: []*onepassword.ItemField{
  1889. {
  1890. ID: field1,
  1891. Label: field1,
  1892. Value: value1,
  1893. Type: onepassword.FieldTypeAddress,
  1894. },
  1895. {
  1896. ID: field2,
  1897. Label: field2,
  1898. Value: value2,
  1899. Type: onepassword.FieldTypeString,
  1900. },
  1901. },
  1902. },
  1903. {
  1904. setupNote: "multiple fields to remove",
  1905. inputFields: []*onepassword.ItemField{
  1906. {
  1907. ID: field3,
  1908. Label: field3,
  1909. Value: value3,
  1910. Type: onepassword.FieldTypeConcealed,
  1911. },
  1912. {
  1913. ID: field1,
  1914. Label: field1,
  1915. Value: value1,
  1916. Type: onepassword.FieldTypeAddress,
  1917. },
  1918. {
  1919. ID: field3,
  1920. Label: field3,
  1921. Value: value3,
  1922. Type: onepassword.FieldTypeCreditCardType,
  1923. },
  1924. {
  1925. ID: field2,
  1926. Label: field2,
  1927. Value: value2,
  1928. Type: onepassword.FieldTypeString,
  1929. },
  1930. {
  1931. ID: field3,
  1932. Label: field3,
  1933. Value: value3,
  1934. Type: onepassword.FieldTypeGender,
  1935. },
  1936. },
  1937. fieldName: field3,
  1938. expectedErr: ErrExpectedOneField,
  1939. expectedFields: nil,
  1940. },
  1941. }
  1942. // run the tests
  1943. for _, tc := range testCases {
  1944. actualOutput, err := updateFieldValue(tc.inputFields, tc.fieldName, tc.newVal)
  1945. if len(actualOutput) != len(tc.expectedFields) {
  1946. t.Errorf("%s: length fields did not match: -expected, +got:\n-%#v\n+%#v\n", tc.setupNote, tc.expectedFields, actualOutput)
  1947. return
  1948. }
  1949. if !errors.Is(err, tc.expectedErr) {
  1950. t.Errorf(errDoesNotMatchMsgF, tc.setupNote, tc.expectedErr, err)
  1951. }
  1952. for i, check := range tc.expectedFields {
  1953. if len(actualOutput) <= i {
  1954. continue
  1955. }
  1956. if !reflect.DeepEqual(check, actualOutput[i]) {
  1957. t.Errorf("%s: fields at position %d did not match: -expected, +got:\n-%#v\n+%#v\n", tc.setupNote, i, check, actualOutput[i])
  1958. }
  1959. }
  1960. }
  1961. }
  1962. func TestGenerateNewItemField(t *testing.T) {
  1963. field := generateNewItemField("property", "testing")
  1964. if !reflect.DeepEqual(field, &onepassword.ItemField{
  1965. Label: "property",
  1966. Type: onepassword.FieldTypeConcealed,
  1967. Value: "testing",
  1968. }) {
  1969. t.Errorf("field did not match: -expected, +got:\n-%#v\n+%#v\n", &onepassword.ItemField{
  1970. Label: "property",
  1971. Type: onepassword.FieldTypeConcealed,
  1972. Value: "testing",
  1973. }, field)
  1974. }
  1975. }
  1976. func TestProviderOnePasswordPushSecret(t *testing.T) {
  1977. // Most logic is tested in the createItem and updateField functions
  1978. // This test is just to make sure the correct functions are called.
  1979. // the correct values are passed to them, and errors are propagated
  1980. type testCase struct {
  1981. vaults map[string]int
  1982. expectedErr error
  1983. setupNote string
  1984. existingItems []onepassword.Item
  1985. val *corev1.Secret
  1986. existingItemsFields map[string][]*onepassword.ItemField
  1987. createValidateFunc func(*onepassword.Item, string) (*onepassword.Item, error)
  1988. updateValidateFunc func(*onepassword.Item, string) (*onepassword.Item, error)
  1989. ref fakeRef
  1990. }
  1991. var (
  1992. vaultName = "vault1"
  1993. vault = onepassword.Vault{
  1994. ID: vaultName,
  1995. }
  1996. )
  1997. testCases := []testCase{
  1998. {
  1999. vaults: map[string]int{
  2000. vaultName: 1,
  2001. },
  2002. expectedErr: ErrExpectedOneItem,
  2003. setupNote: "find item error",
  2004. existingItems: []onepassword.Item{
  2005. {
  2006. Title: key1,
  2007. }, {
  2008. Title: key1,
  2009. }, // can be empty, testing for error with length
  2010. },
  2011. ref: fakeRef{
  2012. key: key1,
  2013. secretKey: key1,
  2014. },
  2015. val: &corev1.Secret{Data: map[string][]byte{key1: []byte("testing")}},
  2016. },
  2017. {
  2018. setupNote: "create item error",
  2019. expectedErr: ErrNoVaults,
  2020. val: &corev1.Secret{Data: map[string][]byte{key1: []byte("testing")}},
  2021. ref: fakeRef{secretKey: key1},
  2022. vaults: nil,
  2023. },
  2024. {
  2025. setupNote: "key not in data",
  2026. expectedErr: ErrKeyNotFound,
  2027. val: &corev1.Secret{Data: map[string][]byte{}},
  2028. ref: fakeRef{secretKey: key1},
  2029. vaults: nil,
  2030. },
  2031. {
  2032. setupNote: "create item success",
  2033. expectedErr: nil,
  2034. val: &corev1.Secret{Data: map[string][]byte{
  2035. key1: []byte("testing"),
  2036. }},
  2037. ref: fakeRef{
  2038. key: key1,
  2039. prop: "prop",
  2040. secretKey: key1,
  2041. },
  2042. vaults: map[string]int{
  2043. vaultName: 1,
  2044. },
  2045. createValidateFunc: func(item *onepassword.Item, s string) (*onepassword.Item, error) {
  2046. validateItem(t, &onepassword.Item{
  2047. Title: key1,
  2048. Category: onepassword.Server,
  2049. Vault: onepassword.ItemVault{
  2050. ID: vaultName,
  2051. },
  2052. Fields: []*onepassword.ItemField{
  2053. generateNewItemField("prop", "testing"),
  2054. },
  2055. }, item)
  2056. return item, nil
  2057. },
  2058. },
  2059. {
  2060. setupNote: "update fields error",
  2061. expectedErr: ErrExpectedOneField,
  2062. val: &corev1.Secret{Data: map[string][]byte{
  2063. "key2": []byte("testing"),
  2064. }},
  2065. ref: fakeRef{
  2066. key: key1,
  2067. prop: "prop",
  2068. secretKey: "key2",
  2069. },
  2070. vaults: map[string]int{
  2071. vaultName: 1,
  2072. },
  2073. existingItemsFields: map[string][]*onepassword.ItemField{
  2074. key1: {
  2075. {
  2076. Label: "prop",
  2077. },
  2078. {
  2079. Label: "prop",
  2080. },
  2081. },
  2082. },
  2083. existingItems: []onepassword.Item{
  2084. {
  2085. Vault: onepassword.ItemVault{
  2086. ID: vaultName,
  2087. },
  2088. ID: key1,
  2089. Title: key1,
  2090. },
  2091. },
  2092. },
  2093. {
  2094. setupNote: "standard update",
  2095. expectedErr: nil,
  2096. val: &corev1.Secret{Data: map[string][]byte{
  2097. "key3": []byte("testing2"),
  2098. }},
  2099. ref: fakeRef{
  2100. key: key1,
  2101. prop: "",
  2102. secretKey: "key3",
  2103. },
  2104. vaults: map[string]int{
  2105. vaultName: 1,
  2106. },
  2107. existingItemsFields: map[string][]*onepassword.ItemField{
  2108. key1: {
  2109. {
  2110. Label: "not-prop",
  2111. },
  2112. },
  2113. },
  2114. updateValidateFunc: func(item *onepassword.Item, s string) (*onepassword.Item, error) {
  2115. expectedItem := &onepassword.Item{
  2116. Vault: onepassword.ItemVault{
  2117. ID: vaultName,
  2118. },
  2119. ID: key1,
  2120. Title: key1,
  2121. Fields: []*onepassword.ItemField{
  2122. {
  2123. Label: "not-prop",
  2124. },
  2125. {
  2126. Label: "password",
  2127. Value: "testing2",
  2128. Type: onepassword.FieldTypeConcealed,
  2129. },
  2130. },
  2131. }
  2132. validateItem(t, expectedItem, item)
  2133. return expectedItem, nil
  2134. },
  2135. existingItems: []onepassword.Item{
  2136. {
  2137. Vault: onepassword.ItemVault{
  2138. ID: vaultName,
  2139. },
  2140. ID: key1,
  2141. Title: key1,
  2142. },
  2143. },
  2144. },
  2145. }
  2146. provider := &ProviderOnePassword{}
  2147. for _, tc := range testCases {
  2148. t.Run(tc.setupNote, func(t *testing.T) {
  2149. // setup
  2150. mockClient := fake.NewMockClient()
  2151. mockClient.MockVaults = map[string][]onepassword.Vault{
  2152. vaultName: {vault},
  2153. }
  2154. mockClient.MockItems = map[string][]onepassword.Item{
  2155. vaultName: tc.existingItems,
  2156. }
  2157. mockClient.MockItemFields = map[string]map[string][]*onepassword.ItemField{
  2158. vaultName: tc.existingItemsFields,
  2159. }
  2160. mockClient.CreateItemValidateFunc = func(item *onepassword.Item, s string) (*onepassword.Item, error) {
  2161. return tc.createValidateFunc(item, s)
  2162. }
  2163. mockClient.UpdateItemValidateFunc = func(item *onepassword.Item, s string) (*onepassword.Item, error) {
  2164. return tc.updateValidateFunc(item, s)
  2165. }
  2166. provider.client = mockClient
  2167. provider.vaults = tc.vaults
  2168. err := provider.PushSecret(context.Background(), tc.val, tc.ref)
  2169. if !errors.Is(err, tc.expectedErr) {
  2170. t.Errorf(errDoesNotMatchMsgF, tc.setupNote, tc.expectedErr, err)
  2171. }
  2172. })
  2173. }
  2174. }