onepassword_test.go 61 KB

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