onepassword_test.go 63 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379
  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. },
  1183. {
  1184. setupNote: "one vault, three items, find by tags",
  1185. provider: &ProviderOnePassword{
  1186. vaults: map[string]int{myVault: 1},
  1187. client: fake.NewMockClient().
  1188. AddPredictableVault(myVault).
  1189. AppendItem(myVaultID, onepassword.Item{
  1190. ID: myItemID,
  1191. Title: myItem,
  1192. Tags: []string{"foo", "bar"},
  1193. Vault: onepassword.ItemVault{ID: myVaultID},
  1194. }).
  1195. AppendItemField(myVaultID, myItemID, onepassword.ItemField{
  1196. Label: key1,
  1197. Value: value1,
  1198. }).
  1199. AppendItemField(myVaultID, myItemID, onepassword.ItemField{
  1200. Label: key2,
  1201. Value: value2,
  1202. }).
  1203. AppendItem(myVaultID, onepassword.Item{
  1204. ID: "my-item-id-2",
  1205. Title: "my-item-2",
  1206. Vault: onepassword.ItemVault{ID: myVaultID},
  1207. Tags: []string{"foo", "baz"},
  1208. }).
  1209. AppendItemField(myVaultID, "my-item-id-2", onepassword.ItemField{
  1210. Label: key3,
  1211. Value: value3,
  1212. }).
  1213. AppendItem(myVaultID, onepassword.Item{
  1214. ID: "my-item-id-3",
  1215. Title: "my-item-3",
  1216. Vault: onepassword.ItemVault{ID: myVaultID},
  1217. Tags: []string{"bang", "bing"},
  1218. }).
  1219. AppendItemField(myVaultID, "my-item-id-3", onepassword.ItemField{
  1220. Label: key4,
  1221. Value: value4,
  1222. }),
  1223. },
  1224. checks: []check{
  1225. {
  1226. checkNote: "find with tags",
  1227. ref: esv1beta1.ExternalSecretFind{
  1228. Path: pointer.To(myItem),
  1229. Tags: map[string]string{
  1230. "foo": "true",
  1231. "bar": "true",
  1232. },
  1233. },
  1234. expectedMap: map[string][]byte{
  1235. key1: []byte(value1),
  1236. key2: []byte(value2),
  1237. },
  1238. expectedErr: nil,
  1239. },
  1240. {
  1241. checkNote: "find with tags and get all",
  1242. ref: esv1beta1.ExternalSecretFind{
  1243. Path: pointer.To(myItem),
  1244. Tags: map[string]string{
  1245. "foo": "true",
  1246. },
  1247. },
  1248. expectedMap: map[string][]byte{
  1249. key1: []byte(value1),
  1250. key2: []byte(value2),
  1251. key3: []byte(value3),
  1252. },
  1253. expectedErr: nil,
  1254. },
  1255. },
  1256. },
  1257. {
  1258. setupNote: "3 vaults, 4 items, 5 files",
  1259. provider: &ProviderOnePassword{
  1260. vaults: map[string]int{myVault: 1, myOtherVault: 2, myNonMatchingVault: 3},
  1261. client: fake.NewMockClient().
  1262. // my-vault
  1263. AddPredictableVault(myVault).
  1264. AppendItem(myVaultID, onepassword.Item{
  1265. ID: myItemID,
  1266. Title: myItem,
  1267. Vault: onepassword.ItemVault{ID: myVaultID},
  1268. Category: documentCategory,
  1269. Files: []*onepassword.File{
  1270. {
  1271. ID: myFilePNGID,
  1272. Name: myFilePNG,
  1273. },
  1274. {
  1275. ID: mySecondFileTXTID,
  1276. Name: mySecondFileTXT,
  1277. },
  1278. },
  1279. }).
  1280. SetFileContents(myFilePNG, []byte(myContents)).
  1281. SetFileContents(mySecondFileTXT, []byte(mySecondContents)).
  1282. AppendItem(myVaultID, onepassword.Item{
  1283. ID: "my-item-2-id",
  1284. Title: "my-item-2",
  1285. Vault: onepassword.ItemVault{ID: myVaultID},
  1286. Category: documentCategory,
  1287. Files: []*onepassword.File{
  1288. {
  1289. ID: myFile2ID,
  1290. Name: myFile2TXT,
  1291. },
  1292. },
  1293. }).
  1294. SetFileContents(myFile2TXT, []byte(myContents2)).
  1295. // my-other-vault
  1296. AddPredictableVault(myOtherVault).
  1297. AppendItem(myOtherVaultID, onepassword.Item{
  1298. ID: myOtherItemID,
  1299. Title: myOtherItem,
  1300. Vault: onepassword.ItemVault{ID: myOtherVaultID},
  1301. Category: documentCategory,
  1302. Files: []*onepassword.File{
  1303. {
  1304. ID: myOtherFilePNGID,
  1305. Name: myOtherFilePNG,
  1306. },
  1307. },
  1308. }).
  1309. SetFileContents(myOtherFilePNG, []byte(myOtherContents)).
  1310. // my-non-matching-vault
  1311. AddPredictableVault(myNonMatchingVault).
  1312. AppendItem(myNonMatchingVaultID, onepassword.Item{
  1313. ID: myNonMatchingItemID,
  1314. Title: myNonMatchingItem,
  1315. Vault: onepassword.ItemVault{ID: myNonMatchingVaultID},
  1316. Category: documentCategory,
  1317. Files: []*onepassword.File{
  1318. {
  1319. ID: nonMatchingFilePNGID,
  1320. Name: nonMatchingFilePNG,
  1321. },
  1322. },
  1323. }).
  1324. SetFileContents(nonMatchingFilePNG, []byte(nonMatchingContents)),
  1325. },
  1326. checks: []check{
  1327. {
  1328. checkNote: "find most with regex '^my-*'",
  1329. ref: esv1beta1.ExternalSecretFind{
  1330. Name: &esv1beta1.FindName{
  1331. RegExp: "^my-*",
  1332. },
  1333. },
  1334. expectedMap: map[string][]byte{
  1335. myFilePNG: []byte(myContents),
  1336. mySecondFileTXT: []byte(mySecondContents),
  1337. myFile2TXT: []byte(myContents2),
  1338. myOtherFilePNG: []byte(myOtherContents),
  1339. },
  1340. expectedErr: nil,
  1341. },
  1342. {
  1343. checkNote: "find some with regex '^my-*' and path 'my-other-item'",
  1344. ref: esv1beta1.ExternalSecretFind{
  1345. Name: &esv1beta1.FindName{
  1346. RegExp: "^my-*",
  1347. },
  1348. Path: pointer.To(myOtherItem),
  1349. },
  1350. expectedMap: map[string][]byte{
  1351. myOtherFilePNG: []byte(myOtherContents),
  1352. },
  1353. expectedErr: nil,
  1354. },
  1355. {
  1356. checkNote: "find none with regex '^asdf*'",
  1357. ref: esv1beta1.ExternalSecretFind{
  1358. Name: &esv1beta1.FindName{
  1359. RegExp: "^asdf*",
  1360. },
  1361. },
  1362. expectedMap: map[string][]byte{},
  1363. expectedErr: nil,
  1364. },
  1365. {
  1366. checkNote: "find none with path 'no-exist'",
  1367. ref: esv1beta1.ExternalSecretFind{
  1368. Name: &esv1beta1.FindName{
  1369. RegExp: "^my-*",
  1370. },
  1371. Path: pointer.To("no-exist"),
  1372. },
  1373. expectedMap: map[string][]byte{},
  1374. expectedErr: nil,
  1375. },
  1376. },
  1377. },
  1378. {
  1379. setupNote: "two fields/files with same name, first one wins",
  1380. provider: &ProviderOnePassword{
  1381. vaults: map[string]int{myVault: 1, myOtherVault: 2},
  1382. client: fake.NewMockClient().
  1383. // my-vault
  1384. AddPredictableVault(myVault).
  1385. AddPredictableItemWithField(myVault, myItem, key1, value1).
  1386. AddPredictableItemWithField(myVault, "my-second-item", key1, "value-second").
  1387. AppendItem(myVaultID, onepassword.Item{
  1388. ID: "file-item-id",
  1389. Title: "file-item",
  1390. Vault: onepassword.ItemVault{ID: myVaultID},
  1391. Category: documentCategory,
  1392. Files: []*onepassword.File{
  1393. {
  1394. ID: filePNGID,
  1395. Name: filePNG,
  1396. },
  1397. },
  1398. }).
  1399. SetFileContents(filePNG, []byte(myContents)).
  1400. AppendItem(myVaultID, onepassword.Item{
  1401. ID: "file-item-2-id",
  1402. Title: "file-item-2",
  1403. Vault: onepassword.ItemVault{ID: myVaultID},
  1404. Category: documentCategory,
  1405. Files: []*onepassword.File{
  1406. {
  1407. ID: "file-2-id",
  1408. Name: filePNG,
  1409. },
  1410. },
  1411. }).
  1412. // my-other-vault
  1413. AddPredictableVault(myOtherVault).
  1414. AddPredictableItemWithField(myOtherVault, myOtherItem, key1, "value-other").
  1415. AppendItem(myOtherVaultID, onepassword.Item{
  1416. ID: "file-item-other-id",
  1417. Title: "file-item-other",
  1418. Vault: onepassword.ItemVault{ID: myOtherVaultID},
  1419. Category: documentCategory,
  1420. Files: []*onepassword.File{
  1421. {
  1422. ID: "other-file-id",
  1423. Name: filePNG,
  1424. },
  1425. },
  1426. }),
  1427. },
  1428. checks: []check{
  1429. {
  1430. checkNote: "find fields with regex '^key*'",
  1431. ref: esv1beta1.ExternalSecretFind{
  1432. Name: &esv1beta1.FindName{
  1433. RegExp: "^key*",
  1434. },
  1435. },
  1436. expectedMap: map[string][]byte{
  1437. key1: []byte(value1),
  1438. },
  1439. expectedErr: nil,
  1440. },
  1441. {
  1442. checkNote: "find files with regex '^file*item*'",
  1443. ref: esv1beta1.ExternalSecretFind{
  1444. Name: &esv1beta1.FindName{
  1445. RegExp: "^file*",
  1446. },
  1447. },
  1448. expectedMap: map[string][]byte{
  1449. filePNG: []byte(myContents),
  1450. },
  1451. expectedErr: nil,
  1452. },
  1453. },
  1454. },
  1455. }
  1456. // run the tests
  1457. for _, tc := range testCases {
  1458. for _, check := range tc.checks {
  1459. gotMap, err := tc.provider.GetAllSecrets(context.Background(), check.ref)
  1460. notes := fmt.Sprintf(setupCheckFormat, tc.setupNote, check.checkNote)
  1461. if check.expectedErr == nil && err != nil {
  1462. // expected no error, got one
  1463. t.Fatalf(getAllSecretsErrFormat, notes, nil, err)
  1464. }
  1465. if check.expectedErr != nil && err == nil {
  1466. // expected an error, didn't get one
  1467. t.Errorf(getAllSecretsErrFormat, notes, check.expectedErr.Error(), nil)
  1468. }
  1469. if check.expectedErr != nil && err != nil && err.Error() != check.expectedErr.Error() {
  1470. // expected an error, got the wrong one
  1471. t.Errorf(getAllSecretsErrFormat, notes, check.expectedErr.Error(), err.Error())
  1472. }
  1473. if !reflect.DeepEqual(check.expectedMap, gotMap) {
  1474. // expected a predefined map, got something else
  1475. t.Errorf(getAllSecretsErrFormat, notes, check.expectedMap, gotMap)
  1476. }
  1477. }
  1478. }
  1479. }
  1480. func TestSortVaults(t *testing.T) {
  1481. type testCase struct {
  1482. vaults map[string]int
  1483. expected []string
  1484. }
  1485. testCases := []testCase{
  1486. {
  1487. vaults: map[string]int{
  1488. one: 1,
  1489. three: 3,
  1490. two: 2,
  1491. },
  1492. expected: []string{
  1493. one,
  1494. two,
  1495. three,
  1496. },
  1497. },
  1498. {
  1499. vaults: map[string]int{
  1500. "four": 100,
  1501. one: 1,
  1502. three: 3,
  1503. two: 2,
  1504. },
  1505. expected: []string{
  1506. one,
  1507. two,
  1508. three,
  1509. "four",
  1510. },
  1511. },
  1512. }
  1513. // run the tests
  1514. for _, tc := range testCases {
  1515. got := sortVaults(tc.vaults)
  1516. if !reflect.DeepEqual(got, tc.expected) {
  1517. t.Errorf("onepassword.sortVaults(...): -expected, +got:\n-%#v\n+%#v\n", tc.expected, got)
  1518. }
  1519. }
  1520. }
  1521. func TestHasUniqueVaultNumbers(t *testing.T) {
  1522. type testCase struct {
  1523. vaults map[string]int
  1524. expected bool
  1525. }
  1526. testCases := []testCase{
  1527. {
  1528. vaults: map[string]int{
  1529. one: 1,
  1530. three: 3,
  1531. two: 2,
  1532. },
  1533. expected: true,
  1534. },
  1535. {
  1536. vaults: map[string]int{
  1537. "four": 100,
  1538. one: 1,
  1539. three: 3,
  1540. two: 2,
  1541. "eight": 100,
  1542. },
  1543. expected: false,
  1544. },
  1545. {
  1546. vaults: map[string]int{
  1547. one: 1,
  1548. "1": 1,
  1549. three: 3,
  1550. two: 2,
  1551. },
  1552. expected: false,
  1553. },
  1554. }
  1555. // run the tests
  1556. for _, tc := range testCases {
  1557. got := hasUniqueVaultNumbers(tc.vaults)
  1558. if got != tc.expected {
  1559. t.Errorf("onepassword.hasUniqueVaultNumbers(...): -expected, +got:\n-%#v\n+%#v\n", tc.expected, got)
  1560. }
  1561. }
  1562. }
  1563. type fakeRef struct {
  1564. key string
  1565. prop string
  1566. secretKey string
  1567. metadata *apiextensionsv1.JSON
  1568. }
  1569. func (f fakeRef) GetRemoteKey() string {
  1570. return f.key
  1571. }
  1572. func (f fakeRef) GetProperty() string {
  1573. return f.prop
  1574. }
  1575. func (f fakeRef) GetSecretKey() string {
  1576. return f.secretKey
  1577. }
  1578. func (f fakeRef) GetMetadata() *apiextensionsv1.JSON {
  1579. return f.metadata
  1580. }
  1581. func validateItem(t *testing.T, expectedItem, actualItem *onepassword.Item) {
  1582. t.Helper()
  1583. if !reflect.DeepEqual(expectedItem, actualItem) {
  1584. t.Errorf("expected item %v, got %v", expectedItem, actualItem)
  1585. }
  1586. }
  1587. func TestProviderOnePasswordCreateItem(t *testing.T) {
  1588. type testCase struct {
  1589. vaults map[string]int
  1590. expectedErr error
  1591. setupNote string
  1592. val []byte
  1593. createValidateFunc func(*testing.T, *onepassword.Item, string) (*onepassword.Item, error)
  1594. ref esv1beta1.PushSecretData
  1595. }
  1596. const vaultName = "vault1"
  1597. const fallbackVaultName = "vault2"
  1598. thridPartyErr := errors.New("third party error")
  1599. metadata := &metadata.PushSecretMetadata[PushSecretMetadataSpec]{
  1600. APIVersion: metadata.APIVersion,
  1601. Kind: metadata.Kind,
  1602. Spec: PushSecretMetadataSpec{
  1603. Tags: []string{"tag1", "tag2"},
  1604. Vault: fallbackVaultName,
  1605. },
  1606. }
  1607. metadataRaw, _ := json.Marshal(metadata)
  1608. testCases := []testCase{
  1609. {
  1610. setupNote: "standard create",
  1611. val: []byte("value"),
  1612. ref: fakeRef{
  1613. key: "testing",
  1614. prop: "prop",
  1615. },
  1616. expectedErr: nil,
  1617. vaults: map[string]int{
  1618. vaultName: 1,
  1619. fallbackVaultName: 2,
  1620. },
  1621. createValidateFunc: func(t *testing.T, item *onepassword.Item, s string) (*onepassword.Item, error) {
  1622. validateItem(t, &onepassword.Item{
  1623. Title: "testing",
  1624. Category: onepassword.Server,
  1625. Vault: onepassword.ItemVault{
  1626. ID: vaultName,
  1627. },
  1628. Fields: []*onepassword.ItemField{
  1629. generateNewItemField("prop", "value"),
  1630. },
  1631. }, item)
  1632. return item, nil
  1633. },
  1634. },
  1635. {
  1636. setupNote: "standard create with no property",
  1637. val: []byte("value2"),
  1638. ref: fakeRef{
  1639. key: "testing2",
  1640. prop: "",
  1641. },
  1642. vaults: map[string]int{
  1643. vaultName: 2,
  1644. },
  1645. createValidateFunc: func(t *testing.T, item *onepassword.Item, s string) (*onepassword.Item, error) {
  1646. validateItem(t, &onepassword.Item{
  1647. Title: "testing2",
  1648. Category: onepassword.Server,
  1649. Vault: onepassword.ItemVault{
  1650. ID: vaultName,
  1651. },
  1652. Fields: []*onepassword.ItemField{
  1653. generateNewItemField("password", "value2"),
  1654. },
  1655. }, item)
  1656. return item, nil
  1657. },
  1658. },
  1659. {
  1660. setupNote: "no vaults",
  1661. val: []byte("value"),
  1662. ref: fakeRef{
  1663. key: "testing",
  1664. prop: "prop",
  1665. },
  1666. vaults: map[string]int{},
  1667. expectedErr: ErrNoVaults,
  1668. createValidateFunc: func(t *testing.T, item *onepassword.Item, s string) (*onepassword.Item, error) {
  1669. t.Errorf("onepassword.createItem(...): should not have been called")
  1670. return nil, nil
  1671. },
  1672. },
  1673. {
  1674. setupNote: "error on create",
  1675. val: []byte("testing"),
  1676. ref: fakeRef{
  1677. key: "another",
  1678. prop: "property",
  1679. },
  1680. vaults: map[string]int{
  1681. vaultName: 1,
  1682. },
  1683. expectedErr: thridPartyErr,
  1684. createValidateFunc: func(t *testing.T, item *onepassword.Item, s string) (*onepassword.Item, error) {
  1685. validateItem(t, &onepassword.Item{
  1686. Title: "another",
  1687. Category: onepassword.Server,
  1688. Vault: onepassword.ItemVault{
  1689. ID: vaultName,
  1690. },
  1691. Fields: []*onepassword.ItemField{
  1692. generateNewItemField("property", "testing"),
  1693. },
  1694. }, item)
  1695. return nil, thridPartyErr
  1696. },
  1697. },
  1698. {
  1699. setupNote: "valid metadata overrides",
  1700. val: []byte("testing"),
  1701. ref: fakeRef{
  1702. key: "another",
  1703. prop: "property",
  1704. metadata: &apiextensionsv1.JSON{
  1705. Raw: metadataRaw,
  1706. },
  1707. },
  1708. vaults: map[string]int{
  1709. vaultName: 1,
  1710. fallbackVaultName: 2,
  1711. },
  1712. expectedErr: nil,
  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: fallbackVaultName,
  1719. },
  1720. Fields: []*onepassword.ItemField{
  1721. generateNewItemField("property", "testing"),
  1722. },
  1723. Tags: []string{"tag1", "tag2"},
  1724. }, item)
  1725. return item, nil
  1726. },
  1727. },
  1728. }
  1729. provider := &ProviderOnePassword{}
  1730. for _, tc := range testCases {
  1731. // setup
  1732. mockClient := fake.NewMockClient()
  1733. mockClient.CreateItemValidateFunc = func(item *onepassword.Item, s string) (*onepassword.Item, error) {
  1734. i, e := tc.createValidateFunc(t, item, s)
  1735. return i, e
  1736. }
  1737. provider.client = mockClient
  1738. provider.vaults = tc.vaults
  1739. err := provider.createItem(tc.val, tc.ref)
  1740. if !errors.Is(err, tc.expectedErr) {
  1741. t.Errorf(errDoesNotMatchMsgF, tc.setupNote, tc.expectedErr, err)
  1742. }
  1743. }
  1744. }
  1745. func TestProviderOnePasswordDeleteItem(t *testing.T) {
  1746. type testCase struct {
  1747. inputFields []*onepassword.ItemField
  1748. fieldName string
  1749. expectedErr error
  1750. expectedFields []*onepassword.ItemField
  1751. setupNote string
  1752. }
  1753. field1, field2, field3, field4 := "field1", "field2", "field3", "field4"
  1754. testCases := []testCase{
  1755. {
  1756. setupNote: "one field to remove",
  1757. inputFields: []*onepassword.ItemField{
  1758. {
  1759. ID: field1,
  1760. Label: field1,
  1761. Type: onepassword.FieldTypeAddress,
  1762. },
  1763. {
  1764. ID: field2,
  1765. Label: field2,
  1766. Type: onepassword.FieldTypeString,
  1767. },
  1768. {
  1769. ID: field3,
  1770. Label: field3,
  1771. Type: onepassword.FieldTypeConcealed,
  1772. },
  1773. },
  1774. fieldName: field2,
  1775. expectedFields: []*onepassword.ItemField{
  1776. {
  1777. ID: field1,
  1778. Label: field1,
  1779. Type: onepassword.FieldTypeAddress,
  1780. },
  1781. {
  1782. ID: field3,
  1783. Label: field3,
  1784. Type: onepassword.FieldTypeConcealed,
  1785. },
  1786. },
  1787. },
  1788. {
  1789. setupNote: "no fields to remove",
  1790. inputFields: []*onepassword.ItemField{
  1791. {
  1792. ID: field1,
  1793. Label: field1,
  1794. Type: onepassword.FieldTypeAddress,
  1795. },
  1796. {
  1797. ID: field2,
  1798. Label: field2,
  1799. Type: onepassword.FieldTypeString,
  1800. },
  1801. {
  1802. ID: field3,
  1803. Label: field3,
  1804. Type: onepassword.FieldTypeConcealed,
  1805. },
  1806. },
  1807. expectedErr: nil,
  1808. fieldName: field4,
  1809. expectedFields: []*onepassword.ItemField{
  1810. {
  1811. ID: field1,
  1812. Label: field1,
  1813. Type: onepassword.FieldTypeAddress,
  1814. },
  1815. {
  1816. ID: field2,
  1817. Label: field2,
  1818. Type: onepassword.FieldTypeString,
  1819. },
  1820. {
  1821. ID: field3,
  1822. Label: field3,
  1823. Type: onepassword.FieldTypeConcealed,
  1824. },
  1825. },
  1826. },
  1827. {
  1828. setupNote: "multiple fields to remove",
  1829. inputFields: []*onepassword.ItemField{
  1830. {
  1831. ID: field3,
  1832. Label: field3,
  1833. Type: onepassword.FieldTypeConcealed,
  1834. },
  1835. {
  1836. ID: field1,
  1837. Label: field1,
  1838. Type: onepassword.FieldTypeAddress,
  1839. },
  1840. {
  1841. ID: field3,
  1842. Label: field3,
  1843. Type: onepassword.FieldTypeCreditCardType,
  1844. },
  1845. {
  1846. ID: field2,
  1847. Label: field2,
  1848. Type: onepassword.FieldTypeString,
  1849. },
  1850. {
  1851. ID: field3,
  1852. Label: field3,
  1853. Type: onepassword.FieldTypeGender,
  1854. },
  1855. },
  1856. fieldName: field3,
  1857. expectedErr: ErrExpectedOneField,
  1858. expectedFields: nil,
  1859. },
  1860. }
  1861. // run the tests
  1862. for _, tc := range testCases {
  1863. actualOutput, err := deleteField(tc.inputFields, tc.fieldName)
  1864. if len(actualOutput) != len(tc.expectedFields) {
  1865. t.Errorf("%s: length fields did not match: -expected, +got:\n-%#v\n+%#v\n", tc.setupNote, tc.expectedFields, actualOutput)
  1866. return
  1867. }
  1868. if !errors.Is(err, tc.expectedErr) {
  1869. t.Errorf(errDoesNotMatchMsgF, tc.setupNote, tc.expectedErr, err)
  1870. }
  1871. for i, check := range tc.expectedFields {
  1872. if len(actualOutput) <= i {
  1873. continue
  1874. }
  1875. if !reflect.DeepEqual(check, actualOutput[i]) {
  1876. t.Errorf("%s: fields at position %d did not match: -expected, +got:\n-%#v\n+%#v\n", tc.setupNote, i, check, actualOutput[i])
  1877. }
  1878. }
  1879. }
  1880. }
  1881. func TestUpdateFields(t *testing.T) {
  1882. type testCase struct {
  1883. inputFields []*onepassword.ItemField
  1884. fieldName string
  1885. newVal string
  1886. expectedErr error
  1887. expectedFields []*onepassword.ItemField
  1888. setupNote string
  1889. }
  1890. field1, field2, field3, field4 := "field1", "field2", "field3", "field4"
  1891. testCases := []testCase{
  1892. {
  1893. setupNote: "one field to update",
  1894. inputFields: []*onepassword.ItemField{
  1895. {
  1896. ID: field1,
  1897. Label: field1,
  1898. Value: value1,
  1899. Type: onepassword.FieldTypeAddress,
  1900. },
  1901. {
  1902. ID: field2,
  1903. Label: field2,
  1904. Value: value2,
  1905. Type: onepassword.FieldTypeString,
  1906. },
  1907. {
  1908. ID: field3,
  1909. Label: field3,
  1910. Value: value3,
  1911. Type: onepassword.FieldTypeConcealed,
  1912. },
  1913. },
  1914. fieldName: field2,
  1915. newVal: "testing",
  1916. expectedFields: []*onepassword.ItemField{
  1917. {
  1918. ID: field1,
  1919. Label: field1,
  1920. Value: value1,
  1921. Type: onepassword.FieldTypeAddress,
  1922. },
  1923. {
  1924. ID: field2,
  1925. Label: field2,
  1926. Value: "testing",
  1927. Type: onepassword.FieldTypeString,
  1928. },
  1929. {
  1930. ID: field3,
  1931. Label: field3,
  1932. Value: value3,
  1933. Type: onepassword.FieldTypeConcealed,
  1934. },
  1935. },
  1936. },
  1937. {
  1938. setupNote: "add field",
  1939. inputFields: []*onepassword.ItemField{
  1940. {
  1941. ID: field1,
  1942. Value: value1,
  1943. Label: field1,
  1944. Type: onepassword.FieldTypeAddress,
  1945. },
  1946. {
  1947. ID: field2,
  1948. Label: field2,
  1949. Value: value2,
  1950. Type: onepassword.FieldTypeString,
  1951. },
  1952. },
  1953. fieldName: field4,
  1954. newVal: value4,
  1955. expectedFields: []*onepassword.ItemField{
  1956. {
  1957. ID: field1,
  1958. Label: field1,
  1959. Value: value1,
  1960. Type: onepassword.FieldTypeAddress,
  1961. },
  1962. {
  1963. ID: field2,
  1964. Label: field2,
  1965. Value: value2,
  1966. Type: onepassword.FieldTypeString,
  1967. },
  1968. {
  1969. Label: field4,
  1970. Value: value4,
  1971. Type: onepassword.FieldTypeConcealed,
  1972. },
  1973. },
  1974. },
  1975. {
  1976. setupNote: "no changes",
  1977. inputFields: []*onepassword.ItemField{
  1978. {
  1979. ID: field1,
  1980. Label: field1,
  1981. Value: value1,
  1982. Type: onepassword.FieldTypeAddress,
  1983. },
  1984. {
  1985. ID: field2,
  1986. Label: field2,
  1987. Value: value2,
  1988. Type: onepassword.FieldTypeString,
  1989. },
  1990. },
  1991. fieldName: field1,
  1992. newVal: value1,
  1993. expectedErr: nil,
  1994. expectedFields: []*onepassword.ItemField{
  1995. {
  1996. ID: field1,
  1997. Label: field1,
  1998. Value: value1,
  1999. Type: onepassword.FieldTypeAddress,
  2000. },
  2001. {
  2002. ID: field2,
  2003. Label: field2,
  2004. Value: value2,
  2005. Type: onepassword.FieldTypeString,
  2006. },
  2007. },
  2008. },
  2009. {
  2010. setupNote: "multiple fields to remove",
  2011. inputFields: []*onepassword.ItemField{
  2012. {
  2013. ID: field3,
  2014. Label: field3,
  2015. Value: value3,
  2016. Type: onepassword.FieldTypeConcealed,
  2017. },
  2018. {
  2019. ID: field1,
  2020. Label: field1,
  2021. Value: value1,
  2022. Type: onepassword.FieldTypeAddress,
  2023. },
  2024. {
  2025. ID: field3,
  2026. Label: field3,
  2027. Value: value3,
  2028. Type: onepassword.FieldTypeCreditCardType,
  2029. },
  2030. {
  2031. ID: field2,
  2032. Label: field2,
  2033. Value: value2,
  2034. Type: onepassword.FieldTypeString,
  2035. },
  2036. {
  2037. ID: field3,
  2038. Label: field3,
  2039. Value: value3,
  2040. Type: onepassword.FieldTypeGender,
  2041. },
  2042. },
  2043. fieldName: field3,
  2044. expectedErr: ErrExpectedOneField,
  2045. expectedFields: nil,
  2046. },
  2047. }
  2048. // run the tests
  2049. for _, tc := range testCases {
  2050. actualOutput, err := updateFieldValue(tc.inputFields, tc.fieldName, tc.newVal)
  2051. if len(actualOutput) != len(tc.expectedFields) {
  2052. t.Errorf("%s: length fields did not match: -expected, +got:\n-%#v\n+%#v\n", tc.setupNote, tc.expectedFields, actualOutput)
  2053. return
  2054. }
  2055. if !errors.Is(err, tc.expectedErr) {
  2056. t.Errorf(errDoesNotMatchMsgF, tc.setupNote, tc.expectedErr, err)
  2057. }
  2058. for i, check := range tc.expectedFields {
  2059. if len(actualOutput) <= i {
  2060. continue
  2061. }
  2062. if !reflect.DeepEqual(check, actualOutput[i]) {
  2063. t.Errorf("%s: fields at position %d did not match: -expected, +got:\n-%#v\n+%#v\n", tc.setupNote, i, check, actualOutput[i])
  2064. }
  2065. }
  2066. }
  2067. }
  2068. func TestGenerateNewItemField(t *testing.T) {
  2069. field := generateNewItemField("property", "testing")
  2070. if !reflect.DeepEqual(field, &onepassword.ItemField{
  2071. Label: "property",
  2072. Type: onepassword.FieldTypeConcealed,
  2073. Value: "testing",
  2074. }) {
  2075. t.Errorf("field did not match: -expected, +got:\n-%#v\n+%#v\n", &onepassword.ItemField{
  2076. Label: "property",
  2077. Type: onepassword.FieldTypeConcealed,
  2078. Value: "testing",
  2079. }, field)
  2080. }
  2081. }
  2082. func TestProviderOnePasswordPushSecret(t *testing.T) {
  2083. // Most logic is tested in the createItem and updateField functions
  2084. // This test is just to make sure the correct functions are called.
  2085. // the correct values are passed to them, and errors are propagated
  2086. type testCase struct {
  2087. vaults map[string]int
  2088. expectedErr error
  2089. setupNote string
  2090. existingItems []onepassword.Item
  2091. val *corev1.Secret
  2092. existingItemsFields map[string][]*onepassword.ItemField
  2093. createValidateFunc func(*onepassword.Item, string) (*onepassword.Item, error)
  2094. updateValidateFunc func(*onepassword.Item, string) (*onepassword.Item, error)
  2095. ref fakeRef
  2096. }
  2097. var (
  2098. vaultName = "vault1"
  2099. vault = onepassword.Vault{
  2100. ID: vaultName,
  2101. }
  2102. )
  2103. metadata := &metadata.PushSecretMetadata[PushSecretMetadataSpec]{
  2104. APIVersion: metadata.APIVersion,
  2105. Kind: metadata.Kind,
  2106. Spec: PushSecretMetadataSpec{
  2107. Tags: []string{"tag1", "tag2"},
  2108. },
  2109. }
  2110. metadataRaw, _ := json.Marshal(metadata)
  2111. testCases := []testCase{
  2112. {
  2113. vaults: map[string]int{
  2114. vaultName: 1,
  2115. },
  2116. expectedErr: ErrExpectedOneItem,
  2117. setupNote: "find item error",
  2118. existingItems: []onepassword.Item{
  2119. {
  2120. Title: key1,
  2121. }, {
  2122. Title: key1,
  2123. }, // can be empty, testing for error with length
  2124. },
  2125. ref: fakeRef{
  2126. key: key1,
  2127. secretKey: key1,
  2128. },
  2129. val: &corev1.Secret{Data: map[string][]byte{key1: []byte("testing")}},
  2130. },
  2131. {
  2132. setupNote: "create item error",
  2133. expectedErr: ErrNoVaults,
  2134. val: &corev1.Secret{Data: map[string][]byte{key1: []byte("testing")}},
  2135. ref: fakeRef{secretKey: key1},
  2136. vaults: nil,
  2137. },
  2138. {
  2139. setupNote: "key not in data",
  2140. expectedErr: ErrKeyNotFound,
  2141. val: &corev1.Secret{Data: map[string][]byte{}},
  2142. ref: fakeRef{secretKey: key1},
  2143. vaults: nil,
  2144. },
  2145. {
  2146. setupNote: "create item success",
  2147. expectedErr: nil,
  2148. val: &corev1.Secret{Data: map[string][]byte{
  2149. key1: []byte("testing"),
  2150. }},
  2151. ref: fakeRef{
  2152. key: key1,
  2153. prop: "prop",
  2154. secretKey: key1,
  2155. },
  2156. vaults: map[string]int{
  2157. vaultName: 1,
  2158. },
  2159. createValidateFunc: func(item *onepassword.Item, s string) (*onepassword.Item, error) {
  2160. validateItem(t, &onepassword.Item{
  2161. Title: key1,
  2162. Category: onepassword.Server,
  2163. Vault: onepassword.ItemVault{
  2164. ID: vaultName,
  2165. },
  2166. Fields: []*onepassword.ItemField{
  2167. generateNewItemField("prop", "testing"),
  2168. },
  2169. }, item)
  2170. return item, nil
  2171. },
  2172. },
  2173. {
  2174. setupNote: "update fields error",
  2175. expectedErr: ErrExpectedOneField,
  2176. val: &corev1.Secret{Data: map[string][]byte{
  2177. "key2": []byte("testing"),
  2178. }},
  2179. ref: fakeRef{
  2180. key: key1,
  2181. prop: "prop",
  2182. secretKey: "key2",
  2183. },
  2184. vaults: map[string]int{
  2185. vaultName: 1,
  2186. },
  2187. existingItemsFields: map[string][]*onepassword.ItemField{
  2188. key1: {
  2189. {
  2190. Label: "prop",
  2191. },
  2192. {
  2193. Label: "prop",
  2194. },
  2195. },
  2196. },
  2197. existingItems: []onepassword.Item{
  2198. {
  2199. Vault: onepassword.ItemVault{
  2200. ID: vaultName,
  2201. },
  2202. ID: key1,
  2203. Title: key1,
  2204. },
  2205. },
  2206. },
  2207. {
  2208. setupNote: "standard update",
  2209. expectedErr: nil,
  2210. val: &corev1.Secret{Data: map[string][]byte{
  2211. "key3": []byte("testing2"),
  2212. }},
  2213. ref: fakeRef{
  2214. key: key1,
  2215. prop: "",
  2216. secretKey: "key3",
  2217. },
  2218. vaults: map[string]int{
  2219. vaultName: 1,
  2220. },
  2221. existingItemsFields: map[string][]*onepassword.ItemField{
  2222. key1: {
  2223. {
  2224. Label: "not-prop",
  2225. },
  2226. },
  2227. },
  2228. updateValidateFunc: func(item *onepassword.Item, s string) (*onepassword.Item, error) {
  2229. expectedItem := &onepassword.Item{
  2230. Vault: onepassword.ItemVault{
  2231. ID: vaultName,
  2232. },
  2233. ID: key1,
  2234. Title: key1,
  2235. Fields: []*onepassword.ItemField{
  2236. {
  2237. Label: "not-prop",
  2238. },
  2239. {
  2240. Label: "password",
  2241. Value: "testing2",
  2242. Type: onepassword.FieldTypeConcealed,
  2243. },
  2244. },
  2245. }
  2246. validateItem(t, expectedItem, item)
  2247. return expectedItem, nil
  2248. },
  2249. existingItems: []onepassword.Item{
  2250. {
  2251. Vault: onepassword.ItemVault{
  2252. ID: vaultName,
  2253. },
  2254. ID: key1,
  2255. Title: key1,
  2256. },
  2257. },
  2258. },
  2259. {
  2260. setupNote: "create item with metadata overwrites success",
  2261. expectedErr: nil,
  2262. val: &corev1.Secret{Data: map[string][]byte{
  2263. key1: []byte("testing"),
  2264. }},
  2265. ref: fakeRef{
  2266. key: key1,
  2267. prop: "prop",
  2268. secretKey: key1,
  2269. metadata: &apiextensionsv1.JSON{
  2270. Raw: metadataRaw,
  2271. },
  2272. },
  2273. vaults: map[string]int{
  2274. vaultName: 1,
  2275. },
  2276. createValidateFunc: func(item *onepassword.Item, s string) (*onepassword.Item, error) {
  2277. validateItem(t, &onepassword.Item{
  2278. Title: key1,
  2279. Category: onepassword.Server,
  2280. Vault: onepassword.ItemVault{
  2281. ID: vaultName,
  2282. },
  2283. Fields: []*onepassword.ItemField{
  2284. generateNewItemField("prop", "testing"),
  2285. },
  2286. Tags: []string{"tag1", "tag2"},
  2287. }, item)
  2288. return item, nil
  2289. },
  2290. },
  2291. }
  2292. provider := &ProviderOnePassword{}
  2293. for _, tc := range testCases {
  2294. t.Run(tc.setupNote, func(t *testing.T) {
  2295. // setup
  2296. mockClient := fake.NewMockClient()
  2297. mockClient.MockVaults = map[string][]onepassword.Vault{
  2298. vaultName: {vault},
  2299. }
  2300. mockClient.MockItems = map[string][]onepassword.Item{
  2301. vaultName: tc.existingItems,
  2302. }
  2303. mockClient.MockItemFields = map[string]map[string][]*onepassword.ItemField{
  2304. vaultName: tc.existingItemsFields,
  2305. }
  2306. mockClient.CreateItemValidateFunc = func(item *onepassword.Item, s string) (*onepassword.Item, error) {
  2307. return tc.createValidateFunc(item, s)
  2308. }
  2309. mockClient.UpdateItemValidateFunc = func(item *onepassword.Item, s string) (*onepassword.Item, error) {
  2310. return tc.updateValidateFunc(item, s)
  2311. }
  2312. provider.client = mockClient
  2313. provider.vaults = tc.vaults
  2314. err := provider.PushSecret(context.Background(), tc.val, tc.ref)
  2315. if !errors.Is(err, tc.expectedErr) {
  2316. t.Errorf(errDoesNotMatchMsgF, tc.setupNote, tc.expectedErr, err)
  2317. }
  2318. })
  2319. }
  2320. }