onepassword_test.go 63 KB

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